aboutsummaryrefslogtreecommitdiff
path: root/compiler_wrapper/clang_tidy_flag.go
diff options
context:
space:
mode:
Diffstat (limited to 'compiler_wrapper/clang_tidy_flag.go')
-rw-r--r--compiler_wrapper/clang_tidy_flag.go225
1 files changed, 184 insertions, 41 deletions
diff --git a/compiler_wrapper/clang_tidy_flag.go b/compiler_wrapper/clang_tidy_flag.go
index 40a5bdbe..01387fd6 100644
--- a/compiler_wrapper/clang_tidy_flag.go
+++ b/compiler_wrapper/clang_tidy_flag.go
@@ -5,15 +5,39 @@
package main
import (
+ "encoding/json"
"fmt"
+ "io/ioutil"
+ "os"
+ "path"
"path/filepath"
"strings"
)
-func processClangTidyFlags(builder *commandBuilder) (cSrcFile string, useClangTidy bool) {
+type useTidyMode int
+
+const clangTidyCrashSubstring = "PLEASE submit a bug report"
+
+const (
+ tidyModeNone useTidyMode = iota
+ tidyModeAll
+ tidyModeTricium
+)
+
+func processClangTidyFlags(builder *commandBuilder) (cSrcFile string, clangTidyFlags []string, mode useTidyMode) {
+ builder.transformArgs(func(arg builderArg) string {
+ const prefix = "-clang-tidy-flag="
+ if !strings.HasPrefix(arg.value, prefix) {
+ return arg.value
+ }
+
+ clangTidyFlags = append(clangTidyFlags, arg.value[len(prefix):])
+ return ""
+ })
+
withTidy, _ := builder.env.getenv("WITH_TIDY")
if withTidy == "" {
- return "", false
+ return "", clangTidyFlags, tidyModeNone
}
srcFileSuffixes := []string{
".c",
@@ -24,58 +48,177 @@ func processClangTidyFlags(builder *commandBuilder) (cSrcFile string, useClangTi
".c++",
}
cSrcFile = ""
+ srcSuffix := ""
lastArg := ""
for _, arg := range builder.args {
- if hasAtLeastOneSuffix(arg.value, srcFileSuffixes) && lastArg != "-o" {
- cSrcFile = arg.value
+ if lastArg != "-o" {
+ for _, suffix := range srcFileSuffixes {
+ if strings.HasSuffix(arg.value, suffix) {
+ srcSuffix = suffix
+ cSrcFile = arg.value
+ break
+ }
+ }
}
lastArg = arg.value
}
- useClangTidy = cSrcFile != ""
- return cSrcFile, useClangTidy
-}
-func runClangTidy(env env, clangCmd *command, cSrcFile string) error {
- defaultTidyChecks := strings.Join([]string{
- "*",
- "google*",
- "-bugprone-narrowing-conversions",
- "-cppcoreguidelines-*",
- "-fuchsia-*",
- "-google-build-using-namespace",
- "-google-default-arguments",
- "-google-explicit-constructor",
- "-google-readability*",
- "-google-runtime-int",
- "-google-runtime-references",
- "-hicpp-avoid-c-arrays",
- "-hicpp-braces-around-statements",
- "-hicpp-no-array-decay",
- "-hicpp-signed-bitwise",
- "-hicpp-uppercase-literal-suffix",
- "-hicpp-use-auto",
- "-llvm-namespace-comment",
- "-misc-non-private-member-variables-in-classes",
- "-misc-unused-parameters",
- "-modernize-*",
- "-readability-*",
- }, ",")
+ if cSrcFile == "" {
+ return "", clangTidyFlags, tidyModeNone
+ }
+ if withTidy == "tricium" {
+ // Files generated from protobufs can result in _many_ clang-tidy complaints, and aren't
+ // worth linting in general. Don't.
+ if strings.HasSuffix(cSrcFile, ".pb"+srcSuffix) {
+ mode = tidyModeNone
+ } else {
+ mode = tidyModeTricium
+ }
+ } else {
+ mode = tidyModeAll
+ }
+ return cSrcFile, clangTidyFlags, mode
+}
+
+func calcClangTidyInvocation(env env, clangCmd *command, cSrcFile string, tidyFlags ...string) (*command, error) {
resourceDir, err := getClangResourceDir(env, clangCmd.Path)
if err != nil {
- return err
+ return nil, err
}
clangTidyPath := filepath.Join(filepath.Dir(clangCmd.Path), "clang-tidy")
- clangTidyCmd := &command{
- Path: clangTidyPath,
- Args: append([]string{
- "-checks=" + defaultTidyChecks,
- cSrcFile,
- "--",
- "-resource-dir=" + resourceDir,
- }, clangCmd.Args...),
+ args := append([]string{}, tidyFlags...)
+ args = append(args, cSrcFile, "--", "-resource-dir="+resourceDir)
+ args = append(args, clangCmd.Args...)
+ return &command{
+ Path: clangTidyPath,
+ Args: args,
EnvUpdates: clangCmd.EnvUpdates,
+ }, nil
+}
+
+func runClangTidyForTricium(env env, clangCmd *command, cSrcFile, fixesDir string, extraTidyFlags []string, crashArtifactsDir string) error {
+ if err := os.MkdirAll(fixesDir, 0777); err != nil {
+ return fmt.Errorf("creating fixes directory at %q: %v", fixesDir, err)
+ }
+
+ f, err := ioutil.TempFile(fixesDir, "lints-")
+ if err != nil {
+ return fmt.Errorf("making tempfile for tidy: %v", err)
+ }
+ f.Close()
+
+ // `f` is an 'anchor'; it ensures we won't create a similarly-named file in the future.
+ // Hence, we can't delete it.
+ fixesFilePath := f.Name() + ".yaml"
+ fixesMetadataPath := f.Name() + ".json"
+
+ // FIXME(gbiv): Remove `-checks=*` when testing is complete; we should defer to .clang-tidy
+ // files, which are both more expressive and more approachable than `-checks=*`.
+ extraTidyFlags = append(extraTidyFlags, "-checks=*", "--export-fixes="+fixesFilePath)
+ clangTidyCmd, err := calcClangTidyInvocation(env, clangCmd, cSrcFile, extraTidyFlags...)
+ if err != nil {
+ return fmt.Errorf("calculating tidy invocation: %v", err)
+ }
+
+ stdstreams := &strings.Builder{}
+ // Note: We pass nil as stdin as we checked before that the compiler
+ // was invoked with a source file argument.
+ exitCode, err := wrapSubprocessErrorWithSourceLoc(clangTidyCmd,
+ env.run(clangTidyCmd, nil, stdstreams, stdstreams))
+ if err != nil {
+ return err
+ }
+
+ type crashOutput struct {
+ CrashReproducerPath string `json:"crash_reproducer_path"`
+ Stdstreams string `json:"stdstreams"`
+ }
+
+ type metadata struct {
+ Args []string `json:"args"`
+ CrashOutput *crashOutput `json:"crash_output"`
+ Executable string `json:"executable"`
+ ExitCode int `json:"exit_code"`
+ LintTarget string `json:"lint_target"`
+ Stdstreams string `json:"stdstreams"`
+ Wd string `json:"wd"`
+ }
+
+ meta := &metadata{
+ Args: clangTidyCmd.Args,
+ CrashOutput: nil,
+ Executable: clangTidyCmd.Path,
+ ExitCode: exitCode,
+ LintTarget: cSrcFile,
+ Stdstreams: stdstreams.String(),
+ Wd: env.getwd(),
+ }
+
+ // Sometimes, clang-tidy crashes. Unfortunately, these don't get funnelled through the
+ // standard clang crash machinery. :(. Try to work with our own.
+ if crashArtifactsDir != "" && strings.Contains(meta.Stdstreams, clangTidyCrashSubstring) {
+ tidyCrashArtifacts := path.Join(crashArtifactsDir, "clang-tidy")
+ if err := os.MkdirAll(tidyCrashArtifacts, 0777); err != nil {
+ return fmt.Errorf("creating crash artifacts directory at %q: %v", tidyCrashArtifacts, err)
+ }
+
+ f, err := ioutil.TempFile(tidyCrashArtifacts, "crash-")
+ if err != nil {
+ return fmt.Errorf("making tempfile for crash output: %v", err)
+ }
+ f.Close()
+
+ reproCmd := &command{}
+ *reproCmd = *clangCmd
+ reproCmd.Args = append(reproCmd.Args, "-E", "-o", f.Name())
+
+ reproOut := &strings.Builder{}
+ _, err = wrapSubprocessErrorWithSourceLoc(reproCmd, env.run(reproCmd, nil, reproOut, reproOut))
+ if err != nil {
+ return fmt.Errorf("attempting to produce a clang-tidy crash reproducer: %v", err)
+ }
+ meta.CrashOutput = &crashOutput{
+ CrashReproducerPath: f.Name(),
+ Stdstreams: reproOut.String(),
+ }
+ }
+
+ f, err = os.Create(fixesMetadataPath)
+ if err != nil {
+ return fmt.Errorf("creating fixes metadata: %v", err)
+ }
+
+ if err := json.NewEncoder(f).Encode(meta); err != nil {
+ return fmt.Errorf("writing fixes metadata: %v", err)
+ }
+
+ if err := f.Close(); err != nil {
+ return fmt.Errorf("finalizing fixes metadata: %v", err)
+ }
+ return nil
+}
+
+func runClangTidy(env env, clangCmd *command, cSrcFile string, extraTidyFlags []string) error {
+ extraTidyFlags = append(extraTidyFlags,
+ "-checks="+strings.Join([]string{
+ "*",
+ "-bugprone-narrowing-conversions",
+ "-cppcoreguidelines-*",
+ "-fuchsia-*",
+ "-google-readability*",
+ "-google-runtime-references",
+ "-hicpp-*",
+ "-llvm-*",
+ "-misc-non-private-member-variables-in-classes",
+ "-misc-unused-parameters",
+ "-modernize-*",
+ "-readability-*",
+ }, ","))
+ clangTidyCmd, err := calcClangTidyInvocation(env, clangCmd, cSrcFile, extraTidyFlags...)
+ if err != nil {
+ return fmt.Errorf("calculating clang-tidy invocation: %v", err)
}
// Note: We pass nil as stdin as we checked before that the compiler