MCPcopy
hub / github.com/uber-go/nilaway / backpropAcrossAssignment

Function backpropAcrossAssignment

assertion/function/assertiontree/backprop.go:277–410  ·  view source on GitHub ↗

backpropAcrossAssignment handles backpropagation for assignments, including special cases such as type switches and range statements. Moreover, it also handles special contracts such as map reads, channel reads, and type assertions by adding appropriate guards to them. Specifically, there are three

(rootNode *RootAssertionNode, lhs, rhs []ast.Expr)

Source from the content-addressed store, hash-verified

275// Phase 3. mark all LHS and RHS as computed.
276// nonnil(lhs, rhs)
277func backpropAcrossAssignment(rootNode *RootAssertionNode, lhs, rhs []ast.Expr) error {
278 // For phase 1 and 2, we will first handle a few special assignments (with early return), then
279 // if none of the special cases are hit, which means it is a normal assignment, we further
280 // delegate the process to other functions depending on if it is many-to-one or one-to-one
281 // assignment for better code clarity.
282 // In all cases, we should do phase 3 before returning, so here we defer a function for phase 3.
283 defer func() {
284 // Phase 3
285 // Now that we've back-propagated across the assignment itself, make sure we can compute
286 // all of the lhs and rhs.
287 for _, rhsVal := range rhs {
288 rootNode.AddComputation(rhsVal)
289 }
290 for _, lhsVal := range lhs {
291 rootNode.AddComputation(lhsVal)
292 }
293 }()
294
295 // Phase 1 and 2
296 // First handle a few special cases, e.g., type switches, type assertions, range statements,
297 // and some cases for "ok" contracts, all of which will have a rhs with length 1.
298 if len(rhs) == 1 {
299 // Here we first strip the parentheses of the rhs to reveal the underlying nodes.
300 rhsNode := ast.Unparen(rhs[0])
301
302 // Type switch `x := y.(type)`, which needs special handling because TypesInfo.Defs
303 // can't find an object for the lhs.
304 // Note that the key distinction between a "type switch" and a "type assertion" in the
305 // AST is whether the `Type` field of the AST node is nil.
306 if r, ok := rhsNode.(*ast.TypeAssertExpr); ok && r.Type == nil {
307 // lhs must have one element, which is *ast.Ident.
308 if len(lhs) != 1 {
309 return errors.New("lhs must have one element for type switches")
310 }
311 lhsIdent := lhs[0].(*ast.Ident)
312 return backpropAcrossTypeSwitch(rootNode, lhsIdent, r.X)
313 }
314
315 if r, ok := rhsNode.(*ast.UnaryExpr); ok {
316 switch r.Op {
317 case token.RANGE:
318 // Range statement of the form `for x := range y`, which is not overly complex to
319 // handle but does involve distinct semantics.
320 return backpropAcrossRange(rootNode, lhs, r.X)
321 case token.AND:
322 if !rootNode.functionContext.functionConfig.EnableStructInitCheck {
323 // This is the case of creating a pointer and assigning it to a variable, e.g., `x := &y`,
324 // where y is a non-pointer type (e.g., y := S{}).
325 // Here, the pointer is always nonnil, so we can just add a ProduceTriggerNever.
326 if rootNode.Pass().ExprBarsNilness(r.X) {
327 if len(lhs) == 1 && !asthelper.IsEmptyExpr(lhs[0]) {
328 rootNode.AddProduction(&annotation.ProduceTrigger{
329 Annotation: &annotation.ProduceTriggerNever{},
330 Expr: lhs[0],
331 })
332 }
333 }
334 }

Callers 1

backpropAcrossNodeFunction · 0.85

Calls 13

IsEmptyExprFunction · 0.92
CallExprFromExprFunction · 0.92
backpropAcrossTypeSwitchFunction · 0.85
backpropAcrossRangeFunction · 0.85
exprAsDeepProducerFunction · 0.85
AddComputationMethod · 0.80
ExprBarsNilnessMethod · 0.80
PassMethod · 0.80
AddProductionMethod · 0.80
AddGuardMatchMethod · 0.80

Tested by

no test coverage detected