Skip to content

Commit 4682a34

Browse files
authored
Do not report nogo diagnostics for cgo generated files (#4081)
**What type of PR is this?** Bug fix **What does this PR do? Why is it needed?** **Which issues(s) does this PR fix?** Fixes #4079 **Other notes for review**
1 parent 199d8e4 commit 4682a34

File tree

4 files changed

+85
-11
lines changed

4 files changed

+85
-11
lines changed

go/private/actions/compilepkg.bzl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ def _run_nogo(
252252
args.add_all(sources, before_each = "-src")
253253
if cgo_go_srcs:
254254
inputs_direct.append(cgo_go_srcs)
255-
args.add_all([cgo_go_srcs], before_each = "-src")
255+
args.add_all([cgo_go_srcs], before_each = "-ignore_src")
256256
if cover_mode:
257257
args.add("-cover_mode", cover_mode)
258258
if recompile_internal_deps:

go/tools/builders/nogo.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@ func nogo(args []string) error {
2020

2121
fs := flag.NewFlagSet("GoNogo", flag.ExitOnError)
2222
goenv := envFlags(fs)
23-
var unfilteredSrcs, recompileInternalDeps multiFlag
23+
var unfilteredSrcs, ignoreSrcs, recompileInternalDeps multiFlag
2424
var deps, facts archiveMultiFlag
2525
var importPath, packagePath, nogoPath, packageListPath string
2626
var testFilter string
2727
var outFactsPath, outLogPath string
2828
var coverMode string
29-
fs.Var(&unfilteredSrcs, "src", ".go, .c, .cc, .m, .mm, .s, or .S file to be filtered and compiled")
29+
fs.Var(&unfilteredSrcs, "src", ".go, .c, .cc, .m, .mm, .s, or .S file to be filtered and checked")
30+
fs.Var(&ignoreSrcs, "ignore_src", ".go, .c, .cc, .m, .mm, .s, or .S file to be filtered and checked, but with its diagnostics ignored")
3031
fs.Var(&deps, "arc", "Import path, package path, and file name of a direct dependency, separated by '='")
3132
fs.Var(&facts, "facts", "Import path, package path, and file name of a direct dependency's nogo facts file, separated by '='")
3233
fs.StringVar(&importPath, "importpath", "", "The import path of the package being compiled. Not passed to the compiler, but may be displayed in debug data.")
@@ -49,7 +50,7 @@ func nogo(args []string) error {
4950
}
5051

5152
// Filter sources.
52-
srcs, err := filterAndSplitFiles(unfilteredSrcs)
53+
srcs, err := filterAndSplitFiles(append(unfilteredSrcs, ignoreSrcs...))
5354
if err != nil {
5455
return err
5556
}
@@ -81,10 +82,10 @@ func nogo(args []string) error {
8182
return err
8283
}
8384

84-
return runNogo(workDir, nogoPath, goSrcs, facts, importPath, importcfgPath, outFactsPath, outLogPath)
85+
return runNogo(workDir, nogoPath, goSrcs, ignoreSrcs, facts, importPath, importcfgPath, outFactsPath, outLogPath)
8586
}
8687

87-
func runNogo(workDir string, nogoPath string, srcs []string, facts []archive, packagePath, importcfgPath, outFactsPath string, outLogPath string) error {
88+
func runNogo(workDir string, nogoPath string, srcs, ignores []string, facts []archive, packagePath, importcfgPath, outFactsPath string, outLogPath string) error {
8889
if len(srcs) == 0 {
8990
// emit_compilepkg expects a nogo facts file, even if it's empty.
9091
// We also need to write the validation output log.
@@ -105,6 +106,9 @@ func runNogo(workDir string, nogoPath string, srcs []string, facts []archive, pa
105106
args = append(args, "-fact", fmt.Sprintf("%s=%s", fact.importPath, fact.file))
106107
}
107108
args = append(args, "-x", outFactsPath)
109+
for _, ignore := range ignores {
110+
args = append(args, "-ignore", ignore)
111+
}
108112
args = append(args, srcs...)
109113

110114
paramsFile := filepath.Join(workDir, "nogo.param")

go/tools/builders/nogo_main.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ func run(args []string) (error, int) {
7777
importcfg := flags.String("importcfg", "", "The import configuration file")
7878
packagePath := flags.String("p", "", "The package path (importmap) of the package being compiled")
7979
xPath := flags.String("x", "", "The archive file where serialized facts should be written")
80+
var ignores multiFlag
81+
flags.Var(&ignores, "ignore", "Names of files to ignore")
8082
flags.Parse(args)
8183
srcs := flags.Args()
8284

@@ -85,7 +87,7 @@ func run(args []string) (error, int) {
8587
return fmt.Errorf("error parsing importcfg: %v", err), nogoError
8688
}
8789

88-
diagnostics, facts, err := checkPackage(analyzers, *packagePath, packageFile, importMap, factMap, srcs)
90+
diagnostics, facts, err := checkPackage(analyzers, *packagePath, packageFile, importMap, factMap, srcs, ignores)
8991
if err != nil {
9092
return fmt.Errorf("error running analyzers: %v", err), nogoError
9193
}
@@ -156,7 +158,7 @@ func readImportCfg(file string) (packageFile map[string]string, importMap map[st
156158
// It returns an empty string if no source code diagnostics need to be printed.
157159
//
158160
// This implementation was adapted from that of golang.org/x/tools/go/checker/internal/checker.
159-
func checkPackage(analyzers []*analysis.Analyzer, packagePath string, packageFile, importMap map[string]string, factMap map[string]string, filenames []string) (string, []byte, error) {
161+
func checkPackage(analyzers []*analysis.Analyzer, packagePath string, packageFile, importMap map[string]string, factMap map[string]string, filenames, ignoreFiles []string) (string, []byte, error) {
160162
// Register fact types and establish dependencies between analyzers.
161163
actions := make(map[*analysis.Analyzer]*action)
162164
var visit func(a *analysis.Analyzer) *action
@@ -211,8 +213,22 @@ func checkPackage(analyzers []*analysis.Analyzer, packagePath string, packageFil
211213
act.pkg = pkg
212214
}
213215

216+
ignoreFilesSet := map[string]struct{}{}
217+
for _, ignore := range ignoreFiles {
218+
ignoreFilesSet[ignore] = struct{}{}
219+
}
214220
// Process nolint directives similar to golangci-lint.
221+
// Also skip over fully ignored files.
215222
for _, f := range pkg.syntax {
223+
if _, ok := ignoreFilesSet[pkg.fset.Position(f.Pos()).Filename]; ok {
224+
for _, act := range actions {
225+
act.nolint = append(act.nolint, &Range{
226+
from: pkg.fset.Position(f.FileStart),
227+
to: pkg.fset.Position(f.End()).Line,
228+
})
229+
}
230+
continue
231+
}
216232
// CommentMap will correctly associate comments to the largest node group
217233
// applicable. This handles inline comments that might trail a large
218234
// assignment and will apply the comment to the entire assignment.

tests/core/nogo/custom/custom_test.go

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ nogo(
3939
":foofuncname",
4040
":importfmt",
4141
":visibility",
42+
":cgogen",
4243
],
4344
# config = "",
4445
visibility = ["//visibility:public"],
@@ -71,6 +72,14 @@ go_library(
7172
visibility = ["//visibility:public"],
7273
)
7374
75+
go_library(
76+
name = "cgogen",
77+
srcs = ["cgogen.go"],
78+
importpath = "cgogenanalyzer",
79+
deps = ["@org_golang_x_tools//go/analysis"],
80+
visibility = ["//visibility:public"],
81+
)
82+
7483
go_library(
7584
name = "has_errors",
7685
srcs = ["has_errors.go"],
@@ -85,6 +94,13 @@ go_library(
8594
deps = [":dep"],
8695
)
8796
97+
go_library(
98+
name = "uses_cgo_clean",
99+
srcs = ["examplepkg/uses_cgo_clean.go"],
100+
importpath = "uses_cgo_clean",
101+
cgo = True,
102+
)
103+
88104
go_library(
89105
name = "uses_cgo_with_errors",
90106
srcs = [
@@ -109,7 +125,7 @@ go_library(
109125
)
110126
111127
-- foofuncname.go --
112-
// importfmt checks for functions named "Foo".
128+
// foofuncname checks for functions named "Foo".
113129
// It has the same package name as another check to test the checks with
114130
// the same package name do not conflict.
115131
package importfmt
@@ -279,6 +295,40 @@ func run(pass *analysis.Pass) (interface{}, error) {
279295
280296
return nil, nil
281297
}
298+
-- cgogen.go --
299+
// cgogen reports diagnostics on files generated by cgo.
300+
package cgogen
301+
302+
import (
303+
"go/ast"
304+
"strings"
305+
306+
"golang.org/x/tools/go/analysis"
307+
)
308+
309+
const doc = "report synthetic diagnostics on files generated by cgo"
310+
311+
var Analyzer = &analysis.Analyzer{
312+
Name: "cgogen",
313+
Run: run,
314+
Doc: doc,
315+
}
316+
317+
func run(pass *analysis.Pass) (interface{}, error) {
318+
for _, f := range pass.Files {
319+
ast.Inspect(f, func(n ast.Node) bool {
320+
switch n := n.(type) {
321+
case *ast.Comment:
322+
if strings.HasPrefix(n.Text, "//go:linkname") || strings.HasPrefix(n.Text, "//go:cgo_import") {
323+
pass.Reportf(n.Pos(), "nogo must not run on code generated by cgo")
324+
}
325+
return true
326+
}
327+
return true
328+
})
329+
}
330+
return nil, nil
331+
}
282332
283333
-- config.json --
284334
{
@@ -462,6 +512,10 @@ func Test(t *testing.T) {
462512
target: "//:no_errors",
463513
wantSuccess: true,
464514
excludes: []string{"no_errors.go"},
515+
}, {
516+
desc: "uses_cgo_clean",
517+
target: "//:uses_cgo_clean",
518+
wantSuccess: true,
465519
},
466520
} {
467521
t.Run(test.desc, func(t *testing.T) {
@@ -477,9 +531,9 @@ func Test(t *testing.T) {
477531
stderr := &bytes.Buffer{}
478532
cmd.Stderr = stderr
479533
if err := cmd.Run(); err == nil && !test.wantSuccess {
480-
t.Fatal("unexpected success")
534+
t.Fatalf("unexpected success\n%s", stderr)
481535
} else if err != nil && test.wantSuccess {
482-
t.Fatalf("unexpected error: %v", err)
536+
t.Fatalf("unexpected error: %v\n%s", err, stderr)
483537
}
484538

485539
for _, pattern := range test.includes {

0 commit comments

Comments
 (0)