From fc2b4b71780935c8f77f05255fcbe3a954e9cfd5 Mon Sep 17 00:00:00 2001 From: Nickita Khylkouski <90287684+nickita-khylkouski@users.noreply.github.com> Date: Tue, 27 Jan 2026 21:17:15 -0800 Subject: [PATCH] Handle explicit type conversions as potential nil producers Fixes #166 When parsing a CallExpr, we now check if it represents an explicit type conversion (e.g., (*Struct)(nil) or StructPtr(nil)). If so, we pass-through the nilability of the converted expression, since the result can be nil if and only if the argument is nil and the target type allows nil. Type conversions to non-nilable types (e.g., int(x)) continue to be treated as non-nil since those types cannot contain nil values. This catches nil dereferences from explicit type conversions that were previously false negatives. --- .../assertiontree/parse_expr_producer.go | 21 +++++-- .../trustedfunc/type_conversion.go | 56 +++++++++++++++++++ 2 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 testdata/src/go.uber.org/trustedfunc/type_conversion.go diff --git a/assertion/function/assertiontree/parse_expr_producer.go b/assertion/function/assertiontree/parse_expr_producer.go index 3a7db8a5..ea5f32dd 100644 --- a/assertion/function/assertiontree/parse_expr_producer.go +++ b/assertion/function/assertiontree/parse_expr_producer.go @@ -231,7 +231,22 @@ func (r *RootAssertionNode) ParseExprAsProducer(expr ast.Expr, doNotTrack bool) return nil, fldReadProduce() case *ast.CallExpr: - // we delay this check until we're sure we have to make it, as it could be expensive + if r.Pass().TypesInfo.Types[expr.Fun].IsType() { + if len(expr.Args) == 0 { + return nil, nil + } + + if r.Pass().ExprBarsNilness(expr.Fun) { + return nil, nil + } + + return r.ParseExprAsProducer(expr.Args[0], false) + } + + if prod := hook.AssumeReturn(r.Pass(), expr); prod != nil { + return nil, []producer.ParsedProducer{producer.ShallowParsedProducer{Producer: prod}} + } + litArgs := func() bool { for _, expr := range expr.Args { if !r.isStable(expr) { @@ -241,10 +256,6 @@ func (r *RootAssertionNode) ParseExprAsProducer(expr ast.Expr, doNotTrack bool) return true } - if prod := hook.AssumeReturn(r.Pass(), expr); prod != nil { - return nil, []producer.ParsedProducer{producer.ShallowParsedProducer{Producer: prod}} - } - // the cases of a function and method call are different enough here that it would be useless // to try to subsume this switch with funcIdentFromCallExpr switch fun := expr.Fun.(type) { diff --git a/testdata/src/go.uber.org/trustedfunc/type_conversion.go b/testdata/src/go.uber.org/trustedfunc/type_conversion.go new file mode 100644 index 00000000..842f4393 --- /dev/null +++ b/testdata/src/go.uber.org/trustedfunc/type_conversion.go @@ -0,0 +1,56 @@ +// Copyright (c) 2024 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package trustedfunc + +type Struct struct { + Field int +} + +type StructPtr *Struct + +func typeConversionBasic() { + var explicit *Struct + explicit = (*Struct)(nil) + _ = explicit.Field // want "deref" + + explicitShort := (*Struct)(nil) + _ = explicitShort.Field // want "deref" + + explicitWithoutParens := StructPtr(nil) + _ = explicitWithoutParens.Field // want "deref" + + var implicit *Struct + implicit = nil + _ = implicit.Field // want "deref" +} + +func typeConversionWithNonNil() { + var s *Struct = &Struct{Field: 42} + explicit := (*Struct)(s) + _ = explicit.Field + + var implicit *Struct + implicit = s + _ = implicit.Field +} + +func typeConversionToNonNilable() { + var i int = 42 + converted := int(i) + _ = converted // Should not report nil dereference + + f := float64(i) + _ = f // Should not report nil dereference +}