Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@ func run(pass *analysis.Pass) (any, error) {
return
}

pass.Reportf(call.Pos(),
"Azure SDK clients should be created with NewFoosClientWithBaseURI and the resource manager endpoint explicitly specified")
pass.Reportf(call.Pos(), "Azure SDK clients should be created with NewFoosClientWithBaseURI and the resource manager endpoint explicitly specified")
})

return nil, nil
Expand Down
3 changes: 1 addition & 2 deletions checks/AZD/AZD001_data_source_empty_set_id/AZD001.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@ func run(pass *analysis.Pass) (any, error) {
return
}

pass.Reportf(call.Pos(),
"data sources should return an error when a resource cannot be found instead of calling SetId with an empty string")
pass.Reportf(call.Pos(), "data sources should return an error when a resource cannot be found instead of calling SetId with an empty string")
})

return nil, nil
Expand Down
3 changes: 1 addition & 2 deletions checks/AZD/AZD002_data_source_mark_as_gone/AZD002.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ func run(pass *analysis.Pass) (any, error) {
return
}

pass.Reportf(sel.Pos(),
"data sources should return an error when a resource cannot be found instead of calling MarkAsGone")
pass.Reportf(sel.Pos(), "data sources should return an error when a resource cannot be found instead of calling MarkAsGone")
})

return nil, nil
Expand Down
3 changes: 1 addition & 2 deletions checks/AZG/AZG000_azignore_missing_reason/AZG000.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ func run(pass *analysis.Pass) (any, error) {
continue
}

pass.Reportf(comment.Pos(),
"azignore directive must include a reason: '//azignore:<Rule> - <reason>'")
pass.Reportf(comment.Pos(), "azignore directive must include a reason: '//azignore:<Rule> - <reason>'")
}
}
}
Expand Down
10 changes: 6 additions & 4 deletions checks/AZG/AZG001_combine_err_assignment_and_check/AZG001.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,22 +68,26 @@ func checkBlock(pass *analysis.Pass, stmts []ast.Stmt) {
lhsIdents = append(lhsIdents, ident)
lhsNames = append(lhsNames, ident.Name)
}

if len(lhsNames) != len(assignStmt.Lhs) {
continue
}
if lhsNames[len(lhsNames)-1] != "err" {
continue
}

allBlankPrefix := true
for _, name := range lhsNames[:len(lhsNames)-1] {
if name != "_" {
allBlankPrefix = false
break
}
}

if !allBlankPrefix {
continue
}

errIdent := lhsIdents[len(lhsIdents)-1]

// Next statement must be `if err != nil`
Expand Down Expand Up @@ -123,10 +127,8 @@ func checkBlock(pass *analysis.Pass, stmts []ast.Stmt) {
}

pass.Report(analysis.Diagnostic{
Pos: assignStmt.Pos(),
Message: fmt.Sprintf(
"'%s' assignment should be combined with the following 'if err != nil' into a single 'if' init statement",
strings.Join(lhsNames, ", ")),
Pos: assignStmt.Pos(),
Message: fmt.Sprintf("'%s' assignment should be combined with the following 'if err != nil' into a single 'if' init statement", strings.Join(lhsNames, ", ")),
SuggestedFixes: fixes,
})
}
Expand Down
21 changes: 11 additions & 10 deletions checks/AZG/AZG002_address_of_single_use_temporary/AZG002.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,10 @@ var fixPointerCopy string

func init() {
Analyzer.Flags.Init("AZG002", flag.ContinueOnError)
Analyzer.Flags.IntVar(&maxGap, "max-gap", 100,
"maximum number of source lines between the temporary's declaration and the statement taking its address")
Analyzer.Flags.StringVar(&use, "use", useNew,
"pointer-creation form to suggest: new or pointer.To (packages below go1.26 must set pointer.To)")
Analyzer.Flags.StringVar(&allow, "allow", "",
"comma-separated forms to leave unreported where they already appear: pointer.To")
Analyzer.Flags.StringVar(&fixPointerCopy, "fix-pointer-copy", fixPointerCopyNone,
"fix for a dereference-copy temporary (out := *p; &out): none, copy, or share")
Analyzer.Flags.IntVar(&maxGap, "max-gap", 100, "maximum number of source lines between the temporary's declaration and the statement taking its address")
Analyzer.Flags.StringVar(&use, "use", useNew, "pointer-creation form to suggest: new or pointer.To (packages below go1.26 must set pointer.To)")
Analyzer.Flags.StringVar(&allow, "allow", "", "comma-separated forms to leave unreported where they already appear: pointer.To")
Analyzer.Flags.StringVar(&fixPointerCopy, "fix-pointer-copy", fixPointerCopyNone, "fix for a dereference-copy temporary (out := *p; &out): none, copy, or share")
}

func run(pass *analysis.Pass) (any, error) {
Expand All @@ -97,6 +93,7 @@ func run(pass *analysis.Pass) (any, error) {
if fixPointerCopy != fixPointerCopyNone && fixPointerCopy != fixPointerCopyCopy && fixPointerCopy != fixPointerCopyShare {
return nil, fmt.Errorf("AZG002: invalid fix-pointer-copy flag %q: expected %q, %q or %q", fixPointerCopy, fixPointerCopyNone, fixPointerCopyCopy, fixPointerCopyShare)
}

allowed := map[string]bool{}
for form := range strings.SplitSeq(allow, ",") {
form = strings.TrimSpace(form)
Expand Down Expand Up @@ -230,11 +227,12 @@ func checkPair(pass *analysis.Pass, body *ast.BlockStmt, first, second ast.Stmt,
if form == usePointerTo {
display = usePointerTo
}

message := fmt.Sprintf("%q is only used as an address by the following statement and should be inlined with %s", ident.Name, display)
if len(between) > 0 {
message = fmt.Sprintf("%q is only used as an address by the statement on line %d and should be inlined with %s",
ident.Name, pass.Fset.Position(second.Pos()).Line, display)
message = fmt.Sprintf("%q is only used as an address by the statement on line %d and should be inlined with %s", ident.Name, pass.Fset.Position(second.Pos()).Line, display)
}

// `v := *p` then `&v` could inline to the copy-preserving pointer.To(*p)/new(*p), but the
// better form is often p itself — dropping the copy changes aliasing, so fix-pointer-copy picks
// the fix: none (default) leaves the choice to a person, copy preserves the copy, share
Expand Down Expand Up @@ -267,6 +265,7 @@ func checkPair(pass *analysis.Pass, body *ast.BlockStmt, first, second ast.Stmt,
}
}
}

pass.Report(analysis.Diagnostic{
Pos: assign.Pos(),
Message: message,
Expand Down Expand Up @@ -308,10 +307,12 @@ func suggestedFixes(pass *analysis.Pass, assign *ast.AssignStmt, addr *ast.Unary
if file == nil {
return nil
}

pkgName, edit, ok := pointerpkg.Ref(file)
if !ok {
return nil
}

creation = pkgName + ".To(" + string(exprSrc) + ")"
importEdit = edit
}
Expand Down
2 changes: 2 additions & 0 deletions checks/AZG/AZG004_zero_value_init_pointer_from/AZG004.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,11 +240,13 @@ func suggestedFixes(pass *analysis.Pass, initStmt ast.Stmt, ifStmt *ast.IfStmt,
if !ok || init.Tok != token.DEFINE || len(init.Lhs) != 1 || len(init.Rhs) != 1 {
return nil
}

lhs, lhsOk := init.Lhs[0].(*ast.Ident)
checked, checkedOk := checkedExpr.(*ast.Ident)
if !lhsOk || !checkedOk || lhs.Name != checked.Name {
return nil
}

fromExpr = init.Rhs[0]
}

Expand Down
7 changes: 3 additions & 4 deletions checks/AZG/AZG005_single_use_temporary/AZG005.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ var maxGap int

func init() {
Analyzer.Flags.Init("AZG005", flag.ContinueOnError)
Analyzer.Flags.IntVar(&maxGap, "max-gap", 100,
"maximum number of source lines between the temporary's declaration and its consumer")
Analyzer.Flags.IntVar(&maxGap, "max-gap", 100, "maximum number of source lines between the temporary's declaration and its consumer")
}

func run(pass *analysis.Pass) (any, error) {
Expand Down Expand Up @@ -142,9 +141,9 @@ func checkPair(pass *analysis.Pass, body *ast.BlockStmt, first, second ast.Stmt,

message := fmt.Sprintf("%q is only used by the following statement and should be inlined", ident.Name)
if !adjacent {
message = fmt.Sprintf("%q is only used by the statement on line %d and should be inlined",
ident.Name, pass.Fset.Position(second.Pos()).Line)
message = fmt.Sprintf("%q is only used by the statement on line %d and should be inlined", ident.Name, pass.Fset.Position(second.Pos()).Line)
}

pass.Report(analysis.Diagnostic{
Pos: assign.Pos(),
Message: message,
Expand Down
13 changes: 5 additions & 8 deletions checks/AZG/AZG006_single_use_call_argument/AZG006.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,9 @@ var maxGap int

func init() {
Analyzer.Flags.Init("AZG006", flag.ContinueOnError)
Analyzer.Flags.IntVar(&maxGap, "max-gap", 100,
"maximum number of source lines between the variable's declaration and the consuming call")
Analyzer.Flags.BoolVar(&onlyWhenLiterals, "only-when-literals", false,
"only inline when every sibling argument is a basic literal (plain identifiers are otherwise also accepted)")
Analyzer.Flags.IntVar(&maximumArguments, "maximum-arguments", 0,
"skip calls with more than this many arguments (0 = unlimited)")
Analyzer.Flags.IntVar(&maxGap, "max-gap", 100, "maximum number of source lines between the variable's declaration and the consuming call")
Analyzer.Flags.BoolVar(&onlyWhenLiterals, "only-when-literals", false, "only inline when every sibling argument is a basic literal (plain identifiers are otherwise also accepted)")
Analyzer.Flags.IntVar(&maximumArguments, "maximum-arguments", 0, "skip calls with more than this many arguments (0 = unlimited)")
}

// onlyWhenLiterals restricts sibling arguments to basic literals; by default plain
Expand Down Expand Up @@ -171,9 +168,9 @@ func checkPair(pass *analysis.Pass, body *ast.BlockStmt, first, second ast.Stmt,

message := fmt.Sprintf("%q is only used by the following call and should be inlined", ident.Name)
if !adjacent {
message = fmt.Sprintf("%q is only used by the call on line %d and should be inlined",
ident.Name, pass.Fset.Position(second.Pos()).Line)
message = fmt.Sprintf("%q is only used by the call on line %d and should be inlined", ident.Name, pass.Fset.Position(second.Pos()).Line)
}

pass.Report(analysis.Diagnostic{
Pos: assign.Pos(),
Message: message,
Expand Down
9 changes: 4 additions & 5 deletions checks/AZG/AZG007_redundant_zero_value_field/AZG007.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ var checkTests bool

func init() {
Analyzer.Flags.Init("AZG007", flag.ContinueOnError)
Analyzer.Flags.BoolVar(&checkTests, "tests", false,
"also report in test files, where a zero entry in a test table is often meaningful (off by default)")
Analyzer.Flags.BoolVar(&checkTests, "tests", false, "also report in test files, where a zero entry in a test table is often meaningful (off by default)")
}

func run(pass *analysis.Pass) (any, error) {
Expand Down Expand Up @@ -97,13 +96,12 @@ func run(pass *analysis.Pass) (any, error) {
if i > 0 {
prevEnd = compositeLit.Elts[i-1].End()
}

prevLine := pass.Fset.Position(prevEnd).Line
kvLine := pass.Fset.Position(kv.Pos()).Line
leadComment := false
for _, cg := range commentGroups {
if cg.Pos() > prevEnd && cg.End() < kv.Pos() &&
pass.Fset.Position(cg.Pos()).Line > prevLine &&
pass.Fset.Position(cg.End()).Line == kvLine-1 {
if cg.Pos() > prevEnd && cg.End() < kv.Pos() && pass.Fset.Position(cg.Pos()).Line > prevLine && pass.Fset.Position(cg.End()).Line == kvLine-1 {
leadComment = true
break
}
Expand Down Expand Up @@ -196,5 +194,6 @@ func isRedundantZero(pass *analysis.Pass, basic *types.Basic, value ast.Expr) bo
case info&types.IsNumeric != 0:
return constant.Sign(cv) == 0
}

return false
}
17 changes: 9 additions & 8 deletions checks/AZG/AZG008_unchecked_nil_dereference/AZG008.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,10 @@ var fixWith string

func init() {
Analyzer.Flags.Init("AZG008", flag.ContinueOnError)
Analyzer.Flags.BoolVar(&includeParameters, "include-parameters", false,
"also report dereferences of bare pointer parameters (callers' nil-check contract is otherwise trusted)")
Analyzer.Flags.BoolVar(&checkTests, "tests", true,
"check _test.go files (false skips them)")
Analyzer.Flags.BoolVar(&reportRequestBody, "requestbody", true,
"report dereferences sent as a PUT/PATCH/POST body (never fixed); false skips them")
Analyzer.Flags.StringVar(&fixWith, "fix-with", fixPointerFrom,
"suggested-fix form: pointer.From or none")
Analyzer.Flags.BoolVar(&includeParameters, "include-parameters", false, "also report dereferences of bare pointer parameters (callers' nil-check contract is otherwise trusted)")
Analyzer.Flags.BoolVar(&checkTests, "tests", true, "check _test.go files (false skips them)")
Analyzer.Flags.BoolVar(&reportRequestBody, "requestbody", true, "report dereferences sent as a PUT/PATCH/POST body (never fixed); false skips them")
Analyzer.Flags.StringVar(&fixWith, "fix-with", fixPointerFrom, "suggested-fix form: pointer.From or none")
}

func run(pass *analysis.Pass) (any, error) {
Expand Down Expand Up @@ -171,6 +167,7 @@ func checkDeref(pass *analysis.Pass, parents map[ast.Node]ast.Node, params map[t
for parents[body] != nil {
body = parents[body]
}

sinks := map[types.Object]bool{}
ast.Inspect(body, func(n ast.Node) bool {
if call, ok := n.(*ast.CallExpr); ok {
Expand Down Expand Up @@ -239,6 +236,7 @@ climb:
case fixWith == fixPointerFrom:
fixes = suggestedFixes(pass, parents, star)
}

pass.Report(analysis.Diagnostic{
Pos: star.Pos(),
Message: msg,
Expand All @@ -263,10 +261,12 @@ func suggestedFixes(pass *analysis.Pass, parents map[ast.Node]ast.Node, star *as
if !ok {
return nil
}

file := astx.EnclosingFile(pass, star.Pos())
if file == nil {
return nil
}

pkgName, importEdit, ok := pointerpkg.Ref(file)
if !ok {
return nil
Expand All @@ -293,6 +293,7 @@ func suggestedFixes(pass *analysis.Pass, parents map[ast.Node]ast.Node, star *as
if importEdit != nil {
edits = append(edits, *importEdit)
}

return []analysis.SuggestedFix{{
Message: "Replace the dereference with " + pkgName + ".From",
TextEdits: edits,
Expand Down
9 changes: 7 additions & 2 deletions checks/AZG/AZG009_pointer_from_enum_conversion/AZG009.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,26 +32,32 @@ func run(pass *analysis.Pass) (any, error) {
if !ok || len(call.Args) != 1 {
return
}

conversion, ok := ast.Unparen(call.Fun).(*ast.Ident)
if !ok || pass.TypesInfo.Uses[conversion] != types.Universe.Lookup("string") {
return
}

from, ok := ast.Unparen(call.Args[0]).(*ast.CallExpr)
if !ok || len(from.Args) != 1 {
return
}

fun := ast.Unparen(from.Fun)
if index, isIndex := fun.(*ast.IndexExpr); isIndex {
fun = ast.Unparen(index.X)
}

sel, ok := fun.(*ast.SelectorExpr)
if !ok {
return
}

fn := astx.CalledFunc(pass, from)
if fn == nil || fn.Name() != "From" || fn.Pkg() == nil || fn.Pkg().Path() != pointerpkg.PkgPath {
return
}

named, ok := types.Unalias(pass.TypesInfo.TypeOf(from)).(*types.Named)
if !ok || !azuresdk.IsEnumType(named) {
return
Expand All @@ -76,8 +82,7 @@ func suggestedFixes(pass *analysis.Pass, call, from *ast.CallExpr, sel *ast.Sele
}
for _, group := range file.Comments {
for _, comment := range group.List {
if comment.Pos() > call.Pos() && comment.End() < call.End() &&
(comment.Pos() < from.Pos() || comment.End() > from.End()) {
if comment.Pos() > call.Pos() && comment.End() < call.End() && (comment.Pos() < from.Pos() || comment.End() > from.End()) {
return nil
}
}
Expand Down
15 changes: 9 additions & 6 deletions checks/AZG/AZG010_unneeded_import_alias/AZG010.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,9 @@ var checkGenerated bool
// split on a separator that it may itself contain
func init() {
Analyzer.Flags.Init("AZG010", flag.ContinueOnError)
Analyzer.Flags.BoolVar(&checkGenerated, "generated", false,
"also check generated files")
Analyzer.Flags.BoolVar(&ownPackage, "own-package", false,
"also report an alias that avoids the name of the file's own package")
Analyzer.Flags.BoolVar(&mismatched, "mismatched", true,
"report an alias that is the package's real name where the import path does not show it (false leaves it alone)")
Analyzer.Flags.BoolVar(&checkGenerated, "generated", false, "also check generated files")
Analyzer.Flags.BoolVar(&ownPackage, "own-package", false, "also report an alias that avoids the name of the file's own package")
Analyzer.Flags.BoolVar(&mismatched, "mismatched", true, "report an alias that is the package's real name where the import path does not show it (false leaves it alone)")
Analyzer.Flags.Func("ignore", "skip imports whose package name matches this regular expression; repeat for several",
func(s string) error {
pattern, err := regexp.Compile(s)
Expand Down Expand Up @@ -114,10 +111,12 @@ func run(pass *analysis.Pass) (any, error) {
if spec.Name == nil || spec.Name.Name == "_" || spec.Name.Name == "." {
continue
}

pkgName, ok := pass.TypesInfo.Defs[spec.Name].(*types.PkgName)
if !ok {
continue
}

alias := spec.Name.Name
name := pkgName.Imported().Name()

Expand All @@ -137,6 +136,7 @@ func run(pass *analysis.Pass) (any, error) {
}
writeName = base != name
}

if alias == name && writeName {
continue
}
Expand Down Expand Up @@ -169,6 +169,7 @@ func run(pass *analysis.Pass) (any, error) {
clash = true
}
}

if clash {
continue
}
Expand Down Expand Up @@ -196,6 +197,7 @@ func run(pass *analysis.Pass) (any, error) {
return true
})
}

if declared[name] {
continue
}
Expand Down Expand Up @@ -233,6 +235,7 @@ func run(pass *analysis.Pass) (any, error) {
}
}
}

if testDeclared[name] {
continue
}
Expand Down
Loading
Loading