blob: ed14719f196e60b9d5751ce0b3cbd525a3016311 [file]
// Copyright IBM Corp. 2014, 2026
// SPDX-License-Identifier: BUSL-1.1
package defectdetector
import (
"go/ast"
"go/types"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/ast/inspector"
)
// DiagsAppendAnalyzer reports ignored return values from tfdiags.Diagnostics.Append.
var DiagsAppendAnalyzer = &analysis.Analyzer{
Name: "tfdiagsappendcheck",
Doc: "reports ignored return values from tfdiags.Diagnostics.Append",
Requires: []*analysis.Analyzer{
inspect.Analyzer,
},
Run: run,
}
func run(pass *analysis.Pass) (interface{}, error) {
ins, ok := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
if !ok {
return nil, nil
}
nodeFilter := []ast.Node{(*ast.ExprStmt)(nil)}
ins.Preorder(nodeFilter, func(n ast.Node) {
exprStmt := n.(*ast.ExprStmt)
call, ok := exprStmt.X.(*ast.CallExpr)
if !ok {
return
}
sel, ok := call.Fun.(*ast.SelectorExpr)
if !ok || sel.Sel == nil || sel.Sel.Name != "Append" {
return
}
selInfo, ok := pass.TypesInfo.Selections[sel]
if !ok || selInfo.Kind() != types.MethodVal {
return
}
if !isTfdiagsDiagnostics(selInfo.Recv()) {
// Ignore calls to other Append methods that aren't tfdiags.Diagnostics.Append.
return
}
pass.Reportf(sel.Sel.Pos(), "ignored return value from tfdiags.Diagnostics.Append")
})
return nil, nil
}
// Check that the receiver type is tfdiags.Diagnostics or *tfdiags.Diagnostics
func isTfdiagsDiagnostics(t types.Type) bool {
if t == nil {
return false
}
if ptr, ok := t.(*types.Pointer); ok {
t = ptr.Elem()
}
named, ok := t.(*types.Named)
if !ok {
return false
}
obj := named.Obj()
if obj == nil || obj.Name() != "Diagnostics" {
return false
}
pkg := obj.Pkg()
if pkg == nil {
return false
}
return pkg.Path() == "github.com/hashicorp/terraform/internal/tfdiags"
}