Index: clang-tools-extra/trunk/clang-tidy/ClangTidyDiagnosticConsumer.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/ClangTidyDiagnosticConsumer.h +++ clang-tools-extra/trunk/clang-tidy/ClangTidyDiagnosticConsumer.h @@ -190,6 +190,15 @@ return AllowEnablingAnalyzerAlphaCheckers; } + using DiagLevelAndFormatString = std::pair; + DiagLevelAndFormatString getDiagLevelAndFormatString(unsigned DiagnosticID, + SourceLocation Loc) { + return DiagLevelAndFormatString( + static_cast( + DiagEngine->getDiagnosticLevel(DiagnosticID, Loc)), + DiagEngine->getDiagnosticIDs()->getDescription(DiagnosticID)); + } + private: // Writes to Stats. friend class ClangTidyDiagnosticConsumer; @@ -242,6 +251,7 @@ class ClangTidyDiagnosticConsumer : public DiagnosticConsumer { public: ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx, + DiagnosticsEngine *ExternalDiagEngine = nullptr, bool RemoveIncompatibleErrors = true); // FIXME: The concept of converting between FixItHints and Replacements is @@ -266,7 +276,10 @@ void checkFilters(SourceLocation Location, const SourceManager &Sources); bool passesLineFilter(StringRef FileName, unsigned LineNumber) const; + void forwardDiagnostic(const Diagnostic &Info); + ClangTidyContext &Context; + DiagnosticsEngine *ExternalDiagEngine; bool RemoveIncompatibleErrors; std::vector Errors; std::unique_ptr HeaderFilter; Index: clang-tools-extra/trunk/clang-tidy/ClangTidyDiagnosticConsumer.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/ClangTidyDiagnosticConsumer.cpp +++ clang-tools-extra/trunk/clang-tidy/ClangTidyDiagnosticConsumer.cpp @@ -275,8 +275,10 @@ } ClangTidyDiagnosticConsumer::ClangTidyDiagnosticConsumer( - ClangTidyContext &Ctx, bool RemoveIncompatibleErrors) - : Context(Ctx), RemoveIncompatibleErrors(RemoveIncompatibleErrors), + ClangTidyContext &Ctx, DiagnosticsEngine *ExternalDiagEngine, + bool RemoveIncompatibleErrors) + : Context(Ctx), ExternalDiagEngine(ExternalDiagEngine), + RemoveIncompatibleErrors(RemoveIncompatibleErrors), LastErrorRelatesToUserCode(false), LastErrorPassesLineFilter(false), LastErrorWasIgnored(false) {} @@ -461,16 +463,22 @@ IsWarningAsError); } - ClangTidyDiagnosticRenderer Converter( - Context.getLangOpts(), &Context.DiagEngine->getDiagnosticOptions(), - Errors.back()); - SmallString<100> Message; - Info.FormatDiagnostic(Message); - FullSourceLoc Loc; - if (Info.getLocation().isValid() && Info.hasSourceManager()) - Loc = FullSourceLoc(Info.getLocation(), Info.getSourceManager()); - Converter.emitDiagnostic(Loc, DiagLevel, Message, Info.getRanges(), - Info.getFixItHints()); + if (ExternalDiagEngine) { + // If there is an external diagnostics engine, like in the + // ClangTidyPluginAction case, forward the diagnostics to it. + forwardDiagnostic(Info); + } else { + ClangTidyDiagnosticRenderer Converter( + Context.getLangOpts(), &Context.DiagEngine->getDiagnosticOptions(), + Errors.back()); + SmallString<100> Message; + Info.FormatDiagnostic(Message); + FullSourceLoc Loc; + if (Info.getLocation().isValid() && Info.hasSourceManager()) + Loc = FullSourceLoc(Info.getLocation(), Info.getSourceManager()); + Converter.emitDiagnostic(Loc, DiagLevel, Message, Info.getRanges(), + Info.getFixItHints()); + } if (Info.hasSourceManager()) checkFilters(Info.getLocation(), Info.getSourceManager()); @@ -494,6 +502,68 @@ return false; } +void ClangTidyDiagnosticConsumer::forwardDiagnostic(const Diagnostic &Info) { + // Acquire a diagnostic ID also in the external diagnostics engine. + auto DiagLevelAndFormatString = + Context.getDiagLevelAndFormatString(Info.getID(), Info.getLocation()); + unsigned ExternalID = ExternalDiagEngine->getDiagnosticIDs()->getCustomDiagID( + DiagLevelAndFormatString.first, DiagLevelAndFormatString.second); + + // Forward the details. + auto builder = ExternalDiagEngine->Report(Info.getLocation(), ExternalID); + for (auto Hint : Info.getFixItHints()) + builder << Hint; + for (auto Range : Info.getRanges()) + builder << Range; + for (unsigned Index = 0; Index < Info.getNumArgs(); ++Index) { + DiagnosticsEngine::ArgumentKind kind = Info.getArgKind(Index); + switch (kind) { + case clang::DiagnosticsEngine::ak_std_string: + builder << Info.getArgStdStr(Index); + break; + case clang::DiagnosticsEngine::ak_c_string: + builder << Info.getArgCStr(Index); + break; + case clang::DiagnosticsEngine::ak_sint: + builder << Info.getArgSInt(Index); + break; + case clang::DiagnosticsEngine::ak_uint: + builder << Info.getArgUInt(Index); + break; + case clang::DiagnosticsEngine::ak_tokenkind: + builder << static_cast(Info.getRawArg(Index)); + break; + case clang::DiagnosticsEngine::ak_identifierinfo: + builder << Info.getArgIdentifier(Index); + break; + case clang::DiagnosticsEngine::ak_qual: + builder << Qualifiers::fromOpaqueValue(Info.getRawArg(Index)); + break; + case clang::DiagnosticsEngine::ak_qualtype: + builder << QualType::getFromOpaquePtr((void *)Info.getRawArg(Index)); + break; + case clang::DiagnosticsEngine::ak_declarationname: + builder << DeclarationName::getFromOpaqueInteger(Info.getRawArg(Index)); + break; + case clang::DiagnosticsEngine::ak_nameddecl: + builder << reinterpret_cast(Info.getRawArg(Index)); + break; + case clang::DiagnosticsEngine::ak_nestednamespec: + builder << reinterpret_cast(Info.getRawArg(Index)); + break; + case clang::DiagnosticsEngine::ak_declcontext: + builder << reinterpret_cast(Info.getRawArg(Index)); + break; + case clang::DiagnosticsEngine::ak_qualtype_pair: + assert(false); // This one is not passed around. + break; + case clang::DiagnosticsEngine::ak_attr: + builder << reinterpret_cast(Info.getRawArg(Index)); + break; + } + } +} + void ClangTidyDiagnosticConsumer::checkFilters(SourceLocation Location, const SourceManager &Sources) { // Invalid location may mean a diagnostic in a command line, don't skip these. Index: clang-tools-extra/trunk/clang-tidy/plugin/ClangTidyPlugin.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/plugin/ClangTidyPlugin.cpp +++ clang-tools-extra/trunk/clang-tidy/plugin/ClangTidyPlugin.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "../ClangTidy.h" +#include "../ClangTidyDiagnosticConsumer.h" #include "../ClangTidyForceLinker.h" #include "../ClangTidyModule.h" #include "clang/Frontend/CompilerInstance.h" @@ -19,29 +20,39 @@ /// The core clang tidy plugin action. This just provides the AST consumer and /// command line flag parsing for using clang-tidy as a clang plugin. class ClangTidyPluginAction : public PluginASTAction { - /// Wrapper to grant the context the same lifetime as the action. We use - /// MultiplexConsumer to avoid writing out all the forwarding methods. + /// Wrapper to grant the context and diagnostics engine the same lifetime as + /// the action. + /// We use MultiplexConsumer to avoid writing out all the forwarding methods. class WrapConsumer : public MultiplexConsumer { std::unique_ptr Context; + std::unique_ptr DiagEngine; public: WrapConsumer(std::unique_ptr Context, + std::unique_ptr DiagEngine, std::vector> Consumer) - : MultiplexConsumer(std::move(Consumer)), Context(std::move(Context)) {} + : MultiplexConsumer(std::move(Consumer)), Context(std::move(Context)), + DiagEngine(std::move(DiagEngine)) {} }; public: std::unique_ptr CreateASTConsumer(CompilerInstance &Compiler, StringRef File) override { - // Insert the current diagnostics engine. - Context->setDiagnosticsEngine(&Compiler.getDiagnostics()); + // Create and set diagnostics engine + auto ExternalDiagEngine = &Compiler.getDiagnostics(); + auto DiagConsumer = + new ClangTidyDiagnosticConsumer(*Context, ExternalDiagEngine); + auto DiagEngine = llvm::make_unique( + new DiagnosticIDs, new DiagnosticOptions, DiagConsumer); + Context->setDiagnosticsEngine(DiagEngine.get()); // Create the AST consumer. ClangTidyASTConsumerFactory Factory(*Context); std::vector> Vec; Vec.push_back(Factory.CreateASTConsumer(Compiler, File)); - return llvm::make_unique(std::move(Context), std::move(Vec)); + return llvm::make_unique( + std::move(Context), std::move(DiagEngine), std::move(Vec)); } bool ParseArgs(const CompilerInstance &, Index: clang-tools-extra/trunk/test/clang-tidy/basic.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/basic.cpp +++ clang-tools-extra/trunk/test/clang-tidy/basic.cpp @@ -1,4 +1,5 @@ // RUN: clang-tidy %s -checks='-*,llvm-namespace-comment' -- | FileCheck %s +// RUN: c-index-test -test-load-source-reparse 2 all %s -Xclang -add-plugin -Xclang clang-tidy -Xclang -plugin-arg-clang-tidy -Xclang -checks='-*,llvm-namespace-comment' 2>&1 | FileCheck %s namespace i { } Index: clang-tools-extra/trunk/test/clang-tidy/nolint-plugin.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/nolint-plugin.cpp +++ clang-tools-extra/trunk/test/clang-tidy/nolint-plugin.cpp @@ -0,0 +1,50 @@ +// REQUIRES: static-analyzer +// RUN: c-index-test -test-load-source-reparse 2 all %s -Xclang -add-plugin -Xclang clang-tidy -Xclang -plugin-arg-clang-tidy -Xclang -checks='-*,google-explicit-constructor,clang-diagnostic-unused-variable,clang-analyzer-core.UndefinedBinaryOperatorResult' -Wunused-variable -I%S/Inputs/nolint 2>&1 | FileCheck %s + +#include "trigger_warning.h" +void I(int& Out) { + int In; + A1(In, Out); +} +// CHECK-NOT: trigger_warning.h:{{.*}} warning +// CHECK-NOT: :[[@LINE-4]]:{{.*}} note + +class A { A(int i); }; +// CHECK-DAG: :[[@LINE-1]]:11: warning: single-argument constructors must be marked explicit + +class B { B(int i); }; // NOLINT + +class C { C(int i); }; // NOLINT(for-some-other-check) +// CHECK-DAG: :[[@LINE-1]]:11: warning: single-argument constructors must be marked explicit + +class C1 { C1(int i); }; // NOLINT(*) + +class C2 { C2(int i); }; // NOLINT(not-closed-bracket-is-treated-as-skip-all + +class C3 { C3(int i); }; // NOLINT(google-explicit-constructor) + +class C4 { C4(int i); }; // NOLINT(some-check, google-explicit-constructor) + +class C5 { C5(int i); }; // NOLINT without-brackets-skip-all, another-check + +void f() { + int i; +// CHECK-DAG: :[[@LINE-1]]:7: warning: unused variable 'i' [-Wunused-variable] +// 31:7: warning: unused variable 'i' [-Wunused-variable] +// int j; // NOLINT +// int k; // NOLINT(clang-diagnostic-unused-variable) +} + +#define MACRO(X) class X { X(int i); }; +MACRO(D) +// CHECK-DAG: :[[@LINE-1]]:7: warning: single-argument constructors must be marked explicit +MACRO(E) // NOLINT + +#define MACRO_NOARG class F { F(int i); }; +MACRO_NOARG // NOLINT + +#define MACRO_NOLINT class G { G(int i); }; // NOLINT +MACRO_NOLINT + +#define DOUBLE_MACRO MACRO(H) // NOLINT +DOUBLE_MACRO Index: clang-tools-extra/trunk/test/clang-tidy/nolintnextline-plugin.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/nolintnextline-plugin.cpp +++ clang-tools-extra/trunk/test/clang-tidy/nolintnextline-plugin.cpp @@ -0,0 +1,48 @@ +// RUN: c-index-test -test-load-source-reparse 2 all %s -Xclang -add-plugin -Xclang clang-tidy -Xclang -plugin-arg-clang-tidy -Xclang -checks='-*,google-explicit-constructor' 2>&1 | FileCheck %s + +class A { A(int i); }; +// CHECK: :[[@LINE-1]]:11: warning: single-argument constructors must be marked explicit + +// NOLINTNEXTLINE +class B { B(int i); }; + +// NOLINTNEXTLINE(for-some-other-check) +class C { C(int i); }; +// CHECK: :[[@LINE-1]]:11: warning: single-argument constructors must be marked explicit + +// NOLINTNEXTLINE(*) +class C1 { C1(int i); }; + +// NOLINTNEXTLINE(not-closed-bracket-is-treated-as-skip-all +class C2 { C2(int i); }; + +// NOLINTNEXTLINE(google-explicit-constructor) +class C3 { C3(int i); }; + +// NOLINTNEXTLINE(some-check, google-explicit-constructor) +class C4 { C4(int i); }; + +// NOLINTNEXTLINE without-brackets-skip-all, another-check +class C5 { C5(int i); }; + + +// NOLINTNEXTLINE + +class D { D(int i); }; +// CHECK: :[[@LINE-1]]:11: warning: single-argument constructors must be marked explicit + +// NOLINTNEXTLINE +// +class E { E(int i); }; +// CHECK: :[[@LINE-1]]:11: warning: single-argument constructors must be marked explicit + +#define MACRO(X) class X { X(int i); }; +MACRO(F) +// CHECK: :[[@LINE-1]]:7: warning: single-argument constructors must be marked explicit +// NOLINTNEXTLINE +MACRO(G) + +#define MACRO_NOARG class H { H(int i); }; +// NOLINTNEXTLINE +MACRO_NOARG +