diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp index 2a680cdcad156..f32470407abc5 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp @@ -124,7 +124,7 @@ bool ChecksFilter::isCheckEnabled(StringRef Name) { ClangTidyContext::ClangTidyContext(SmallVectorImpl *Errors, const ClangTidyOptions &Options) - : Errors(Errors), DiagEngine(nullptr), Filter(Options) {} + : Errors(Errors), DiagEngine(nullptr), Options(Options), Filter(Options) {} DiagnosticBuilder ClangTidyContext::diag( StringRef CheckName, SourceLocation Loc, StringRef Description, @@ -171,7 +171,8 @@ StringRef ClangTidyContext::getCheckName(unsigned DiagnosticID) const { } ClangTidyDiagnosticConsumer::ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx) - : Context(Ctx), LastErrorRelatesToUserCode(false) { + : Context(Ctx), HeaderFilter(Ctx.getOptions().HeaderFilterRegex), + LastErrorRelatesToUserCode(false) { IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); Diags.reset(new DiagnosticsEngine( IntrusiveRefCntPtr(new DiagnosticIDs), &*DiagOpts, this, @@ -217,12 +218,30 @@ void ClangTidyDiagnosticConsumer::HandleDiagnostic( Sources); // Let argument parsing-related warnings through. - if (!Info.getLocation().isValid() || - !Diags->getSourceManager().isInSystemHeader(Info.getLocation())) { + if (relatesToUserCode(Info.getLocation())) { LastErrorRelatesToUserCode = true; } } +bool ClangTidyDiagnosticConsumer::relatesToUserCode(SourceLocation Location) { + // Invalid location may mean a diagnostic in a command line, don't skip these. + if (!Location.isValid()) + return true; + + const SourceManager &Sources = Diags->getSourceManager(); + if (Sources.isInSystemHeader(Location)) + return false; + + // FIXME: We start with a conservative approach here, but the actual type of + // location needed depends on the check (in particular, where this check wants + // to apply fixes). + FileID FID = Sources.getDecomposedExpansionLoc(Location).first; + if (FID == Sources.getMainFileID()) + return true; + + return HeaderFilter.match(Sources.getFileEntryForID(FID)->getName()); +} + struct LessClangTidyError { bool operator()(const ClangTidyError *LHS, const ClangTidyError *RHS) const { const ClangTidyMessage &M1 = LHS->Message; diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h index 6ba690ef245c5..d944836c1b1d9 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h +++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h @@ -10,6 +10,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_DIAGNOSTIC_CONSUMER_H #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_DIAGNOSTIC_CONSUMER_H +#include "ClangTidyOptions.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/SourceManager.h" #include "clang/Tooling/Refactoring.h" @@ -28,8 +29,6 @@ class CompilationDatabase; namespace tidy { -struct ClangTidyOptions; - /// \brief A message from a clang-tidy check. /// /// Note that this is independent of a \c SourceManager. @@ -108,6 +107,7 @@ class ClangTidyContext { StringRef getCheckName(unsigned DiagnosticID) const; ChecksFilter &getChecksFilter() { return Filter; } + const ClangTidyOptions &getOptions() const { return Options; } private: friend class ClangTidyDiagnosticConsumer; // Calls storeError(). @@ -117,6 +117,7 @@ class ClangTidyContext { SmallVectorImpl *Errors; DiagnosticsEngine *DiagEngine; + ClangTidyOptions Options; ChecksFilter Filter; llvm::DenseMap CheckNamesByDiagnosticID; @@ -142,8 +143,10 @@ class ClangTidyDiagnosticConsumer : public DiagnosticConsumer { private: void finalizeLastError(); + bool relatesToUserCode(SourceLocation Location); ClangTidyContext &Context; + llvm::Regex HeaderFilter; std::unique_ptr Diags; SmallVector Errors; bool LastErrorRelatesToUserCode; diff --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.h b/clang-tools-extra/clang-tidy/ClangTidyOptions.h index 582dc78563669..f3e5fd22df93c 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyOptions.h +++ b/clang-tools-extra/clang-tidy/ClangTidyOptions.h @@ -10,6 +10,8 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_OPTIONS_H #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANG_TIDY_OPTIONS_H +#include + namespace clang { namespace tidy { @@ -18,6 +20,9 @@ struct ClangTidyOptions { ClangTidyOptions() : EnableChecksRegex(".*"), AnalyzeTemporaryDtors(false) {} std::string EnableChecksRegex; std::string DisableChecksRegex; + // Output warnings from headers matching this filter. Warnings from main files + // will always be displayed. + std::string HeaderFilterRegex; bool AnalyzeTemporaryDtors; }; diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp index 3d5c312fe7d56..17d1c78607929 100644 --- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp +++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp @@ -39,6 +39,12 @@ static cl::opt DisableChecks( "|llvm-namespace-comment" // Not complete. "|google-.*)"), // Doesn't apply to LLVM. cl::cat(ClangTidyCategory)); +static cl::opt HeaderFilter( + "header-filter", + cl::desc("Regular expression matching the names of the headers to output\n" + "diagnostics from. Diagnostics from the main file of each\n" + "translation unit are always displayed."), + cl::init(""), cl::cat(ClangTidyCategory)); static cl::opt Fix("fix", cl::desc("Fix detected errors if possible."), cl::init(false), cl::cat(ClangTidyCategory)); @@ -59,6 +65,7 @@ int main(int argc, const char **argv) { clang::tidy::ClangTidyOptions Options; Options.EnableChecksRegex = Checks; Options.DisableChecksRegex = DisableChecks; + Options.HeaderFilterRegex = HeaderFilter; Options.AnalyzeTemporaryDtors = AnalyzeTemporaryDtors; // FIXME: Allow using --list-checks without positional arguments. diff --git a/clang-tools-extra/test/clang-tidy/Inputs/file-filter/header1.h b/clang-tools-extra/test/clang-tidy/Inputs/file-filter/header1.h new file mode 100644 index 0000000000000..ba9e3d153ee63 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/Inputs/file-filter/header1.h @@ -0,0 +1 @@ +class A1 { A1(int); }; diff --git a/clang-tools-extra/test/clang-tidy/Inputs/file-filter/header2.h b/clang-tools-extra/test/clang-tidy/Inputs/file-filter/header2.h new file mode 100644 index 0000000000000..09bfeabc8723b --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/Inputs/file-filter/header2.h @@ -0,0 +1 @@ +class A2 { A2(int); }; diff --git a/clang-tools-extra/test/clang-tidy/file-filter.cpp b/clang-tools-extra/test/clang-tidy/file-filter.cpp new file mode 100644 index 0000000000000..f9e029b2692de --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/file-filter.cpp @@ -0,0 +1,22 @@ +// RUN: clang-tidy -checks=google-explicit-constructor -disable-checks='' -header-filter='' %s -- -I %S/Inputs/file-filter | FileCheck %s +// RUN: clang-tidy -checks=google-explicit-constructor -disable-checks='' -header-filter='.*' %s -- -I %S/Inputs/file-filter | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-tidy -checks=google-explicit-constructor -disable-checks='' -header-filter='header2\.h' %s -- -I %S/Inputs/file-filter | FileCheck --check-prefix=CHECK3 %s + +#include "header1.h" +// CHECK-NOT: warning: +// CHECK2: header1.h:1:12: warning: Single-argument constructors must be explicit [google-explicit-constructor] +// CHECK3-NOT: warning: + +#include "header2.h" +// CHECK-NOT: warning: +// CHECK2: header2.h:1:12: warning: Single-argument constructors {{.*}} +// CHECK3: header2.h:1:12: warning: Single-argument constructors {{.*}} + +class A { A(int); }; +// CHECK: :[[@LINE-1]]:11: warning: Single-argument constructors {{.*}} +// CHECK2: :[[@LINE-2]]:11: warning: Single-argument constructors {{.*}} +// CHECK3: :[[@LINE-3]]:11: warning: Single-argument constructors {{.*}} + +// CHECK-NOT: warning: +// CHECK2-NOT: warning: +// CHECK3-NOT: warning: