handleErrorReturns handles the special case for error returning functions (n-th result of type `error` which guards at least one of the first n-1 non-error results). It generates consumers by applying the error contract: (1) if error return value = nil, create consumers for the non-error returns (2)
(rootNode *RootAssertionNode, retStmt *ast.ReturnStmt, results []ast.Expr, isNamedReturn bool)
| 274 | // |
| 275 | // Note that `results` should be explicitly passed since `retStmt` of a named return will contain no results |
| 276 | func handleErrorReturns(rootNode *RootAssertionNode, retStmt *ast.ReturnStmt, results []ast.Expr, isNamedReturn bool) bool { |
| 277 | if !typeshelper.FuncIsErrReturning(rootNode.FuncObj().Signature()) { |
| 278 | return false |
| 279 | } |
| 280 | |
| 281 | errRetIndex := len(results) - 1 |
| 282 | errRetExpr := results[errRetIndex] // n-th expression |
| 283 | nonErrRetExpr := results[:errRetIndex] // n-1 expressions |
| 284 | |
| 285 | // default tracking to support potential "always safe" cases |
| 286 | createReturnConsumersForAlwaysSafe(rootNode, nonErrRetExpr, retStmt, isNamedReturn) |
| 287 | |
| 288 | // check if the error return is at all guarding any nilable returns, such as pointers, maps, and slices |
| 289 | if isErrorReturnNil(rootNode, errRetExpr) { |
| 290 | // if error is the only return expression in the statement, then create a consumer for it, else create consumers for the non-error return expressions |
| 291 | if len(nonErrRetExpr) == 0 { |
| 292 | createConsumerForErrorReturn(rootNode, errRetExpr, errRetIndex, retStmt, isNamedReturn) |
| 293 | } else { |
| 294 | // create general return consume triggers for all n-1 (non-error) return expressions |
| 295 | createGeneralReturnConsumers(rootNode, nonErrRetExpr, retStmt, isNamedReturn) |
| 296 | } |
| 297 | |
| 298 | // TODO: handle struct init in the context of error return in a better way in a follow up diff |
| 299 | if rootNode.functionContext.functionConfig.EnableStructInitCheck { |
| 300 | for i := range results { |
| 301 | rootNode.addConsumptionsForFieldsOfReturns(results[i], i) |
| 302 | } |
| 303 | } |
| 304 | } else if isErrorReturnNonnil(rootNode, errRetExpr) { |
| 305 | // create consume trigger for only the error return |
| 306 | createConsumerForErrorReturn(rootNode, errRetExpr, errRetIndex, retStmt, isNamedReturn) |
| 307 | } else { |
| 308 | // the nilability of error return is unknown, hence create special consume triggers for all returns |
| 309 | createSpecialConsumersForAllReturns(rootNode, nonErrRetExpr, errRetExpr, errRetIndex, retStmt, isNamedReturn) |
| 310 | |
| 311 | // TODO: handle struct init in the context of error return in a better way in a follow up diff |
| 312 | if rootNode.functionContext.functionConfig.EnableStructInitCheck { |
| 313 | for i := range results { |
| 314 | rootNode.addConsumptionsForFieldsOfReturns(results[i], i) |
| 315 | } |
| 316 | } |
| 317 | } |
| 318 | return true |
| 319 | } |
| 320 | |
| 321 | // handleBooleanReturns handles the special case for boolean (`ok`) returning functions (n-th result of type `bool` |
| 322 | // which guards at least one of the first n-1 non-bool results). Similar to the handling of error returning functions, |
no test coverage detected