diff --git a/hook/error_return.go b/hook/error_return.go index 62c2ab03..e9b25dbb 100644 --- a/hook/error_return.go +++ b/hook/error_return.go @@ -86,4 +86,14 @@ var _errorReturnNonnilArgs = map[trustedSig]struct { enclosingRegex: regexp.MustCompile(`^encoding/(json|xml)$`), nameRegex: regexp.MustCompile(`^Unmarshal$`), }: {action: pointeeOfArg, argIndex: 1}, + + // `(cadence).Future.Get(ctx, &v)` blocks until the future is ready and, on success (a nil error + // return), populates the value pointed to by `&v`, so a nil error return implies `v != nil`. The + // `Future` interface lives in `go.uber.org/cadence/internal` and is re-exported (via a type alias) + // as `go.uber.org/cadence/workflow.Future`, so the method's declaring package is the internal one. + { + kind: _method, + enclosingRegex: regexp.MustCompile(`^(stubs/)?go\.uber\.org/cadence/internal\.Future$`), + nameRegex: regexp.MustCompile(`^Get$`), + }: {action: pointeeOfArg, argIndex: 1}, } diff --git a/testdata/src/go.uber.org/trustedfunc/inference/cadence.go b/testdata/src/go.uber.org/trustedfunc/inference/cadence.go new file mode 100644 index 00000000..1be8b70c --- /dev/null +++ b/testdata/src/go.uber.org/trustedfunc/inference/cadence.go @@ -0,0 +1,38 @@ +package inference + +import "stubs/go.uber.org/cadence/workflow" + +// testCadenceFutureGet exercises the `ErrorReturnNonnilArg` hook for cadence's `Future.Get`: +// `f.Get(ctx, &v)` populates `v`, so the pointee is treated as non-nil once the error return is +// checked to be nil. +func testCadenceFutureGet(ctx workflow.Context, f workflow.Future) { + // `err != nil` early return: pointee is non-nil on the fallthrough (error-is-nil) path. + var v1 *int + if err := f.Get(ctx, &v1); err != nil { + return + } + print(*v1) // safe + + // `err == nil` positive check: pointee is non-nil inside the block. + var v2 *int + err := f.Get(ctx, &v2) + if err == nil { + print(*v2) // safe + } + + // Error return not checked at all: no guarantee. + var v3 *int + f.Get(ctx, &v3) + print(*v3) //want "unassigned variable `v3` dereferenced" + + // Error return discarded into the blank identifier: no guarantee. + var v4 *int + _ = f.Get(ctx, &v4) + print(*v4) //want "unassigned variable `v4` dereferenced" + + // Dereference on the error path (`err != nil`): pointee is not guarded here. + var v5 *int + if err := f.Get(ctx, &v5); err != nil { + print(*v5) //want "unassigned variable `v5` dereferenced" + } +} diff --git a/testdata/src/stubs/go.uber.org/cadence/internal/internal.go b/testdata/src/stubs/go.uber.org/cadence/internal/internal.go new file mode 100644 index 00000000..cf854f18 --- /dev/null +++ b/testdata/src/stubs/go.uber.org/cadence/internal/internal.go @@ -0,0 +1,30 @@ +// Copyright (c) 2026 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 internal is a minimal stub of `go.uber.org/cadence/internal`, which is where the `Future` +// interface (re-exported as `go.uber.org/cadence/workflow.Future`) is actually declared. +package internal + +// Context is a minimal stub of cadence's workflow context. +type Context interface { + Value(key interface{}) interface{} +} + +// Future is a minimal stub of cadence's `Future` interface. Its `Get` method blocks until the +// future is ready and, on success (a nil error return), populates the value pointed to by +// `valuePtr` (passed by address as `&v`). +type Future interface { + Get(ctx Context, valuePtr interface{}) error + IsReady() bool +} diff --git a/testdata/src/stubs/go.uber.org/cadence/workflow/workflow.go b/testdata/src/stubs/go.uber.org/cadence/workflow/workflow.go new file mode 100644 index 00000000..8cada5bb --- /dev/null +++ b/testdata/src/stubs/go.uber.org/cadence/workflow/workflow.go @@ -0,0 +1,26 @@ +// Copyright (c) 2026 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 workflow is a minimal stub of `go.uber.org/cadence/workflow`. Like the real package, it +// re-exports the `Future` and `Context` types from the internal package via type aliases, so a call +// to `f.Get(ctx, &v)` resolves to the method declared in `go.uber.org/cadence/internal`. +package workflow + +import "stubs/go.uber.org/cadence/internal" + +// Context aliases internal.Context, mirroring the real cadence package. +type Context = internal.Context + +// Future aliases internal.Future, mirroring the real cadence package. +type Future = internal.Future