Index: clang-tidy/ClangTidy.cpp =================================================================== --- clang-tidy/ClangTidy.cpp +++ clang-tidy/ClangTidy.cpp @@ -302,7 +302,7 @@ typedef std::vector> CheckersList; -static CheckersList getCheckersControlList(ClangTidyContext &Context) { +static CheckersList getCheckersControlList(const ClangTidyContext &Context) { CheckersList List; const auto &RegisteredCheckers = @@ -340,6 +340,7 @@ Context.setSourceManager(&Compiler.getSourceManager()); Context.setCurrentFile(File); Context.setASTContext(&Compiler.getASTContext()); + Context.setPreprocessor(&Compiler.getPreprocessor()); auto WorkingDir = Compiler.getSourceManager() .getFileManager() @@ -397,6 +398,9 @@ CheckNames.push_back(CheckFactory.first); } + if (Context.isCheckEnabled(NolintCheckName)) + CheckNames.push_back(NolintCheckName); + for (const auto &AnalyzerCheck : getCheckersControlList(Context)) CheckNames.push_back(AnalyzerCheckNamePrefix + AnalyzerCheck.first); Index: clang-tidy/ClangTidyDiagnosticConsumer.h =================================================================== --- clang-tidy/ClangTidyDiagnosticConsumer.h +++ clang-tidy/ClangTidyDiagnosticConsumer.h @@ -19,11 +19,15 @@ #include "llvm/ADT/StringMap.h" #include "llvm/Support/Regex.h" #include "llvm/Support/Timer.h" +#include +#include namespace clang { class ASTContext; class CompilerInstance; +class Preprocessor; + namespace ast_matchers { class MatchFinder; } @@ -32,6 +36,7 @@ } namespace tidy { +class NolintCommentsCollector; /// \brief A detected error complete with information to display diagnostic and /// automatic fix. @@ -131,13 +136,24 @@ /// \brief Sets ASTContext for the current translation unit. void setASTContext(ASTContext *Context); + /// \brief Subscribes the context to the preprocessor events if necessary. + void setPreprocessor(Preprocessor *preprocessor); + /// \brief Gets the language options from the AST context. const LangOptions &getLangOpts() const { return LangOpts; } + /// \brief Checks if a check with the specified name exists. + bool hasCheck(StringRef CheckName); + /// \brief Returns the name of the clang-tidy check which produced this /// diagnostic ID. StringRef getCheckName(unsigned DiagnosticID) const; + /// \brief Returns the name of diagnostics as it will be output in report + /// for the specified diagnostic ID. It can be either a check name or + /// clang diagnostics with "clang-diagnostic-" prefix. + std::string getDiagnosticsName(unsigned DiagnosticID) const; + /// \brief Returns \c true if the check is enabled for the \c CurrentFile. /// /// The \c CurrentFile can be changed using \c setCurrentFile. @@ -187,7 +203,7 @@ } private: - // Calls setDiagnosticsEngine() and storeError(). + // Calls setDiagnosticsEngine(), storeError(), and getNolintCollector(). friend class ClangTidyDiagnosticConsumer; friend class ClangTidyPluginAction; @@ -198,8 +214,11 @@ /// \brief Store an \p Error. void storeError(const ClangTidyError &Error); + // \brief Provides access to the \c NOLINT/NOLINTNEXTLINE info collector. + NolintCommentsCollector &getNolintCollector(); + std::vector Errors; - DiagnosticsEngine *DiagEngine; + DiagnosticsEngine *DiagEngine = nullptr; std::unique_ptr OptionsProvider; std::string CurrentFile; @@ -216,7 +235,10 @@ llvm::DenseMap CheckNamesByDiagnosticID; - ProfileData *Profile; + ProfileData *Profile = nullptr; + + std::unique_ptr NolintCollector; + std::vector AllCheckNamesCache; }; /// \brief A diagnostic consumer that turns each \c Diagnostic into a @@ -241,8 +263,17 @@ private: void finalizeLastError(); + bool isLineMarkedWithNolintInMacro(SourceManager &SM, SourceLocation Loc, + StringRef CheckName); + bool lineIsMarkedWithNolint(SourceManager &SM, SourceLocation Loc, + StringRef CheckName); + bool isNolintFound(StringRef NolintDirectiveText, StringRef Line, + StringRef CheckName); + void removeIncompatibleErrors(SmallVectorImpl &Errors) const; + void reportRedundantNolintComments(); + /// \brief Returns the \c HeaderFilter constructed for the options set in the /// context. llvm::Regex *getHeaderFilter(); @@ -256,12 +287,16 @@ bool RemoveIncompatibleErrors; std::unique_ptr Diags; SmallVector Errors; + // Unfortunately, line numbers are not stored in Errors, so gather them + // separately to process NOLINT misuse. + std::unordered_map> DiagIDsToLineNumbers; std::unique_ptr HeaderFilter; - bool LastErrorRelatesToUserCode; - bool LastErrorPassesLineFilter; - bool LastErrorWasIgnored; + bool LastErrorRelatesToUserCode = false; + bool LastErrorPassesLineFilter = false; + bool LastErrorWasIgnored = false; }; +extern const StringRef NolintCheckName; } // end namespace tidy } // end namespace clang Index: clang-tidy/ClangTidyDiagnosticConsumer.cpp =================================================================== --- clang-tidy/ClangTidyDiagnosticConsumer.cpp +++ clang-tidy/ClangTidyDiagnosticConsumer.cpp @@ -16,18 +16,26 @@ /// //===----------------------------------------------------------------------===// +#include "ClangTidy.h" #include "ClangTidyDiagnosticConsumer.h" +#include "ClangTidyModule.h" #include "ClangTidyOptions.h" #include "clang/AST/ASTDiagnostic.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Frontend/DiagnosticRenderer.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/iterator_range.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include #include #include using namespace clang; using namespace tidy; +const StringRef clang::tidy::NolintCheckName = "readability-nolint-usage"; namespace { class ClangTidyDiagnosticRenderer : public DiagnosticRenderer { public: @@ -105,6 +113,70 @@ private: ClangTidyError &Error; }; + +const StringRef ClangDiagnosticsPrefix = "clang-diagnostic-"; +enum class NolintCommentType : uint8_t { + Nolint, + NolintNextLine, +}; +const StringRef NolintComment = "NOLINT"; +const StringRef NolintNextLineComment = "NOLINTNEXTLINE"; + +class NolintCommentParser { +public: + using CheckNamesRange = + llvm::iterator_range::const_iterator>; + + NolintCommentParser(StringRef NolintDirectiveText, StringRef Line) { + if ((NolintIndex = Line.find(NolintDirectiveText)) != StringRef::npos) { + size_t BracketIndex = NolintIndex + NolintDirectiveText.size(); + // Check if the specific checks are specified in brackets. + if (BracketIndex < Line.size() && Line[BracketIndex] == '(') { + ++BracketIndex; + size_t BracketEndIndex = Line.find(')', BracketIndex); + if ((ClosingBracketFound = BracketEndIndex != StringRef::npos)) { + StringRef ChecksStr = + Line.substr(BracketIndex, BracketEndIndex - BracketIndex); + // Allow disabling all the checks with "*". + if (ChecksStr != "*") { + // Allow specifying a few check names, delimited with comma. + ChecksStr.split(CheckNames, ',', -1, false); + llvm::transform(CheckNames, CheckNames.begin(), + [](StringRef S) { return S.trim(); }); + } + } + } + } + } + + bool isNolintFound() const { + return NolintIndex != StringRef::npos; + } + + size_t getNolintIndex() const { + assert(isNolintFound()); + return NolintIndex; + } + + bool isAppliedToAllChecks() const { + return CheckNames.empty(); + } + + CheckNamesRange сheckNames() const { + assert(isNolintFound()); + assert(!isAppliedToAllChecks()); + return llvm::make_range(CheckNames.begin(), CheckNames.end()); + } + + bool isClosingBracketFound() const { + return ClosingBracketFound; + } + +private: + SmallVector CheckNames; + size_t NolintIndex = 0; + bool ClosingBracketFound = true; +}; } // end anonymous namespace ClangTidyError::ClangTidyError(StringRef CheckName, @@ -176,10 +248,86 @@ llvm::StringMap Cache; }; +class clang::tidy::NolintCommentsCollector : public CommentHandler { +public: + struct NolintInfo { + unsigned LineNumber; + StringRef CheckName; + SourceLocation Location; + NolintCommentType Type; + bool isActual; + }; + using NolintInfos = std::vector; + + static const StringRef AnyCheck; + + explicit NolintCommentsCollector(ClangTidyContext *Context) + : Context(Context) {} + + NolintInfos takeNolintInfos() { + NolintInfos result; + std::swap(result, NolintComments); + return result; + } + +private: + bool HandleComment(Preprocessor &PP, SourceRange Range) override { + SourceManager &SrcMgr = PP.getSourceManager(); + StringRef Text = Lexer::getSourceText(CharSourceRange::getCharRange(Range), + SrcMgr, PP.getLangOpts()); + // Search for NOLINTNEXTLINE first (as NOLINT could be a part of it). + StringRef CommentName = NolintNextLineComment; + NolintCommentType CommentType = NolintCommentType::NolintNextLine; + unsigned LineLocationOffset = 1; + NolintCommentParser NolintParser(CommentName, Text); + if (!NolintParser.isNolintFound()) { + // Search for NOLINT otherwise. + CommentName = NolintComment; + CommentType = NolintCommentType::Nolint; + LineLocationOffset = 0; + NolintParser = NolintCommentParser(CommentName, Text); + } + if (NolintParser.isNolintFound()) { + SourceLocation NolintLoc = + Range.getBegin().getLocWithOffset(NolintParser.getNolintIndex()); + unsigned LineNo = + SrcMgr.getExpansionLineNumber(Range.getBegin()) + LineLocationOffset; + static const SmallVector AllChecks = { AnyCheck }; + for (StringRef CheckName : NolintParser.isAppliedToAllChecks() ? + AllChecks : NolintParser.сheckNames()) { + // Don't report diagnostics to itself. + if (CheckName == NolintCheckName) + continue; + if (!CheckName.empty() && !Context->hasCheck(CheckName) && + !CheckName.startswith(ClangDiagnosticsPrefix)) { + Context->diag(NolintCheckName, NolintLoc, + "unknown check name '%0' specified in %1 comment") + << CheckName << CommentName; + } else { + NolintComments.emplace_back( + NolintInfo{LineNo, CheckName, NolintLoc, CommentType, true}); + } + } + if (!NolintParser.isClosingBracketFound()) { + Context->diag(NolintCheckName, NolintLoc, + "unbalanced parentheses in %0 comment; all diagnostics silenced " + "for this line") + << CommentName; + } + } + return false; + } + +private: + NolintInfos NolintComments; + ClangTidyContext *Context; +}; +const StringRef NolintCommentsCollector::AnyCheck; + ClangTidyContext::ClangTidyContext( std::unique_ptr OptionsProvider) - : DiagEngine(nullptr), OptionsProvider(std::move(OptionsProvider)), - Profile(nullptr) { + : OptionsProvider(std::move(OptionsProvider)), + NolintCollector(llvm::make_unique(this)) { // Before the first translation unit we can get errors related to command-line // parsing, use empty string for the file name in this case. setCurrentFile(""); @@ -218,6 +366,12 @@ LangOpts = Context->getLangOpts(); } +void ClangTidyContext::setPreprocessor(Preprocessor *ActualPreprocessor) { + if (ActualPreprocessor && isCheckEnabled(NolintCheckName)) { + ActualPreprocessor->addCommentHandler(NolintCollector.get()); + } +} + const ClangTidyGlobalOptions &ClangTidyContext::getGlobalOptions() const { return OptionsProvider->getGlobalOptions(); } @@ -250,6 +404,25 @@ Errors.push_back(Error); } +NolintCommentsCollector &ClangTidyContext::getNolintCollector() { + return *NolintCollector; +} + +// This method is not const, because it fills cache on first demand. +// It is possible to fill the cache in constructor, but as it is not always +// used, so it is preferred to do it lazily. +// Making cache mutable is not enough, because creation of +// ClangTidyASTConsumerFactory also requires nonconstant ClangTidyContext. +bool ClangTidyContext::hasCheck(StringRef CheckName) { + if (AllCheckNamesCache.empty()) { + ClangTidyASTConsumerFactory Factory(*this); + AllCheckNamesCache = Factory.getCheckNames(); + assert(std::is_sorted(AllCheckNamesCache.begin(), AllCheckNamesCache.end())); + } + return std::binary_search(AllCheckNamesCache.begin(), + AllCheckNamesCache.end(), CheckName); +} + StringRef ClangTidyContext::getCheckName(unsigned DiagnosticID) const { llvm::DenseMap::const_iterator I = CheckNamesByDiagnosticID.find(DiagnosticID); @@ -258,11 +431,17 @@ return ""; } +std::string ClangTidyContext::getDiagnosticsName(unsigned DiagnosticID) const { + StringRef WarningOption = + DiagEngine->getDiagnosticIDs()->getWarningOptionForDiag(DiagnosticID); + return !WarningOption.empty() + ? (ClangDiagnosticsPrefix + WarningOption).str() + : getCheckName(DiagnosticID).str(); +} + ClangTidyDiagnosticConsumer::ClangTidyDiagnosticConsumer( ClangTidyContext &Ctx, bool RemoveIncompatibleErrors) - : Context(Ctx), RemoveIncompatibleErrors(RemoveIncompatibleErrors), - LastErrorRelatesToUserCode(false), LastErrorPassesLineFilter(false), - LastErrorWasIgnored(false) { + : Context(Ctx), RemoveIncompatibleErrors(RemoveIncompatibleErrors) { IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); Diags = llvm::make_unique( IntrusiveRefCntPtr(new DiagnosticIDs), &*DiagOpts, this, @@ -291,38 +470,25 @@ LastErrorPassesLineFilter = false; } -static bool IsNOLINTFound(StringRef NolintDirectiveText, StringRef Line, - unsigned DiagID, const ClangTidyContext &Context) { - const size_t NolintIndex = Line.find(NolintDirectiveText); - if (NolintIndex == StringRef::npos) +bool ClangTidyDiagnosticConsumer::isNolintFound(StringRef NolintDirectiveText, + StringRef Line, + StringRef CheckName) { + NolintCommentParser NolintParser(NolintDirectiveText, Line); + if (!NolintParser.isNolintFound()) { return false; - - size_t BracketIndex = NolintIndex + NolintDirectiveText.size(); - // Check if the specific checks are specified in brackets. - if (BracketIndex < Line.size() && Line[BracketIndex] == '(') { - ++BracketIndex; - const size_t BracketEndIndex = Line.find(')', BracketIndex); - if (BracketEndIndex != StringRef::npos) { - StringRef ChecksStr = - Line.substr(BracketIndex, BracketEndIndex - BracketIndex); - // Allow disabling all the checks with "*". - if (ChecksStr != "*") { - StringRef CheckName = Context.getCheckName(DiagID); - // Allow specifying a few check names, delimited with comma. - SmallVector Checks; - ChecksStr.split(Checks, ',', -1, false); - llvm::transform(Checks, Checks.begin(), - [](StringRef S) { return S.trim(); }); - return llvm::find(Checks, CheckName) != Checks.end(); - } - } } - return true; + if (NolintParser.isAppliedToAllChecks()) { + // NOLINT without check name shouldn't block NolintCheck itself, because + // otherwise there will never be diagnostics on "// NOLINT". + return CheckName != NolintCheckName; + } + const auto &CheckNames = NolintParser.сheckNames(); + return llvm::find(CheckNames, CheckName) != CheckNames.end(); } -static bool LineIsMarkedWithNOLINT(SourceManager &SM, SourceLocation Loc, - unsigned DiagID, - const ClangTidyContext &Context) { +bool ClangTidyDiagnosticConsumer::lineIsMarkedWithNolint(SourceManager &SM, + SourceLocation Loc, + StringRef CheckName) { bool Invalid; const char *CharacterData = SM.getCharacterData(Loc, &Invalid); if (Invalid) @@ -333,7 +499,7 @@ while (*P != '\0' && *P != '\r' && *P != '\n') ++P; StringRef RestOfLine(CharacterData, P - CharacterData + 1); - if (IsNOLINTFound("NOLINT", RestOfLine, DiagID, Context)) + if (isNolintFound(NolintComment, RestOfLine, CheckName)) return true; // Check if there's a NOLINTNEXTLINE on the previous line. @@ -360,17 +526,16 @@ --P; RestOfLine = StringRef(P, LineEnd - P + 1); - if (IsNOLINTFound("NOLINTNEXTLINE", RestOfLine, DiagID, Context)) + if (isNolintFound(NolintNextLineComment, RestOfLine, CheckName)) return true; return false; } -static bool LineIsMarkedWithNOLINTinMacro(SourceManager &SM, SourceLocation Loc, - unsigned DiagID, - const ClangTidyContext &Context) { +bool ClangTidyDiagnosticConsumer::isLineMarkedWithNolintInMacro( + SourceManager &SM, SourceLocation Loc, StringRef CheckName) { while (true) { - if (LineIsMarkedWithNOLINT(SM, Loc, DiagID, Context)) + if (lineIsMarkedWithNolint(SM, Loc, CheckName)) return true; if (!Loc.isMacroID()) return false; @@ -384,33 +549,31 @@ if (LastErrorWasIgnored && DiagLevel == DiagnosticsEngine::Note) return; - if (Info.getLocation().isValid() && DiagLevel != DiagnosticsEngine::Error && - DiagLevel != DiagnosticsEngine::Fatal && - LineIsMarkedWithNOLINTinMacro(Diags->getSourceManager(), - Info.getLocation(), Info.getID(), - Context)) { - ++Context.Stats.ErrorsIgnoredNOLINT; - // Ignored a warning, should ignore related notes as well - LastErrorWasIgnored = true; - return; + std::string CheckName = Context.getDiagnosticsName(Info.getID()); + SourceLocation DiagLocation = Info.getLocation(); + if (DiagLocation.isValid()) { + // This is necessary for finding redundant NOLINT comments. Fill collection + // here, as there won't be another chance if diagnostics are filtered. + DiagIDsToLineNumbers[Info.getID()].push_back( + Info.getSourceManager().getExpansionLineNumber(DiagLocation)); + + if (DiagLevel != DiagnosticsEngine::Error && + DiagLevel != DiagnosticsEngine::Fatal && + isLineMarkedWithNolintInMacro(Diags->getSourceManager(), DiagLocation, + CheckName)) { + ++Context.Stats.ErrorsIgnoredNOLINT; + // Ignored a warning, should ignore related notes as well. + LastErrorWasIgnored = true; + return; + } } LastErrorWasIgnored = false; // Count warnings/errors. DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info); - if (DiagLevel == DiagnosticsEngine::Note) { - assert(!Errors.empty() && - "A diagnostic note can only be appended to a message."); - } else { + if (DiagLevel != DiagnosticsEngine::Note) { finalizeLastError(); - StringRef WarningOption = - Context.DiagEngine->getDiagnosticIDs()->getWarningOptionForDiag( - Info.getID()); - std::string CheckName = !WarningOption.empty() - ? ("clang-diagnostic-" + WarningOption).str() - : Context.getCheckName(Info.getID()).str(); - if (CheckName.empty()) { // This is a compiler diagnostic without a warning option. Assign check // name based on its level. @@ -441,6 +604,9 @@ Context.treatAsError(CheckName); Errors.emplace_back(CheckName, Level, Context.getCurrentBuildDirectory(), IsWarningAsError); + } else { + assert(!Errors.empty() && + "A diagnostic note can only be appended to a message."); } ClangTidyDiagnosticRenderer Converter( @@ -641,6 +807,59 @@ } } +void ClangTidyDiagnosticConsumer::reportRedundantNolintComments() { + NolintCommentsCollector::NolintInfos NolintInfos = + Context.getNolintCollector().takeNolintInfos(); + if (NolintInfos.empty()) + return; + + // For fast lookups the collection should be ordered. + auto NolintInfoComparator = + [] (const NolintCommentsCollector::NolintInfo &Info1, + const NolintCommentsCollector::NolintInfo &Info2) { + return std::tie(Info1.LineNumber, Info1.CheckName) < + std::tie(Info2.LineNumber, Info2.CheckName); + }; + std::sort(NolintInfos.begin(), NolintInfos.end(), NolintInfoComparator); + + // Mark the comments on lines, which there were diagnostics on. + auto MarkNotActualNolint = + [&NolintInfos, &NolintInfoComparator](unsigned LineNumber, + StringRef CheckName) { + auto resultIt = std::lower_bound(NolintInfos.begin(), NolintInfos.end(), + NolintCommentsCollector::NolintInfo{LineNumber, CheckName, + // These values are not taken into account by comparator, + // so they can be anything. + SourceLocation(), NolintCommentType(), true}, + NolintInfoComparator); + // lower_bound doesn't guarantee existence, so check it manually. + if (resultIt != NolintInfos.end() && + resultIt->LineNumber == LineNumber && + resultIt->CheckName == CheckName) { + resultIt->isActual = false; + } + }; + for (const auto &DiagIDToLineNumbers : DiagIDsToLineNumbers) { + std::string CheckName = Context.getDiagnosticsName(DiagIDToLineNumbers.first); + if (CheckName.empty()) + continue; + for (unsigned LineNumber : DiagIDToLineNumbers.second) { + MarkNotActualNolint(LineNumber, CheckName); + MarkNotActualNolint(LineNumber, NolintCommentsCollector::AnyCheck); + } + } + + // Reports diagnostics on the rest of comments. + for (const auto &NolintEntry : NolintInfos) { + if (NolintEntry.isActual) + Context.diag(NolintCheckName, NolintEntry.Location, "there are no " + "diagnostics %select{|for '%2'}0 on %select{this|the next}1 line") + << (NolintEntry.CheckName == NolintCommentsCollector::AnyCheck ? 0 : 1) + << (uint8_t)NolintEntry.Type + << NolintEntry.CheckName; + } +} + namespace { struct LessClangTidyError { bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const { @@ -661,6 +880,7 @@ // Flushes the internal diagnostics buffer to the ClangTidyContext. void ClangTidyDiagnosticConsumer::finish() { + reportRedundantNolintComments(); finalizeLastError(); std::sort(Errors.begin(), Errors.end(), LessClangTidyError()); @@ -673,4 +893,5 @@ for (const ClangTidyError &Error : Errors) Context.storeError(Error); Errors.clear(); + DiagIDsToLineNumbers.clear(); } Index: test/clang-tidy/nolint.cpp =================================================================== --- test/clang-tidy/nolint.cpp +++ test/clang-tidy/nolint.cpp @@ -30,6 +30,9 @@ int i; // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: unused variable 'i' [clang-diagnostic-unused-variable] int j; // NOLINT + int k; // NOLINT(clang-diagnostic-unused-variable) + int l; // NOLINT(clang-diagnostic-literal-conversion) +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: unused variable 'l' [clang-diagnostic-unused-variable] } #define MACRO(X) class X { X(int i); }; @@ -46,4 +49,4 @@ #define DOUBLE_MACRO MACRO(H) // NOLINT DOUBLE_MACRO -// CHECK-MESSAGES: Suppressed 12 warnings (12 NOLINT) +// CHECK-MESSAGES: Suppressed 13 warnings (13 NOLINT) Index: test/clang-tidy/nolintnextline.cpp =================================================================== --- test/clang-tidy/nolintnextline.cpp +++ test/clang-tidy/nolintnextline.cpp @@ -24,6 +24,18 @@ class C5 { C5(int i); }; +void f() { + int i; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: unused variable 'i' [clang-diagnostic-unused-variable] + // NOLINTNEXTLINE + int j; + // NOLINTNEXTLINE(clang-diagnostic-unused-variable) + int k; + // NOLINTNEXTLINE(clang-diagnostic-literal-conversion) + int l; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: unused variable 'l' [clang-diagnostic-unused-variable] +} + // NOLINTNEXTLINE class D { D(int i); }; @@ -44,6 +56,6 @@ // NOLINTNEXTLINE MACRO_NOARG -// CHECK-MESSAGES: Suppressed 8 warnings (8 NOLINT) +// CHECK-MESSAGES: Suppressed 10 warnings (10 NOLINT) -// RUN: %check_clang_tidy %s google-explicit-constructor %t -- +// RUN: %check_clang_tidy %s google-explicit-constructor,clang-diagnostic-unused-variable %t -- -extra-arg=-Wunused-variable -- Index: test/clang-tidy/readability-nolint-usage.cpp =================================================================== --- /dev/null +++ test/clang-tidy/readability-nolint-usage.cpp @@ -0,0 +1,171 @@ +// RUN: %check_clang_tidy %s readability-nolint-usage,google-explicit-constructor,misc-unused-parameters %t -- + +// Case: NO_LINT on line with an error. +class A1 { A1(int i); }; // NOLINT + +// Case: NO_LINT on line without errors. +class A2 { explicit A2(int i); }; // NOLINT +// CHECK-MESSAGES: :[[@LINE-1]]:38: warning: there are no diagnostics on this line + +// Case: NO_LINT for the specific check on line with an error on it. +class A3 { A3(int i); }; // NOLINT(google-explicit-constructor) + +// Case: NO_LINT for the specific check on line with an error on another check. +class A4 { A4(int i); }; // NOLINT(misc-unused-parameters) +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: single-argument constructors must be marked explicit +// CHECK-MESSAGES: :[[@LINE-2]]:29: warning: there are no diagnostics for 'misc-unused-parameters' on this line + +// Case: NO_LINT for the specific check on line without errors. +class A5 { explicit A5(int i); }; // NOLINT(google-explicit-constructor) +// CHECK-MESSAGES: :[[@LINE-1]]:38: warning: there are no diagnostics for 'google-explicit-constructor' on this line + +// Case: NO_LINT for unknown check on line with an error on some check. +class A6 { A6(int i); }; // NOLINT(unknown-check) +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: single-argument constructors must be marked explicit +// CHECK-MESSAGES: :[[@LINE-2]]:29: warning: unknown check name 'unknown-check' specified in NOLINT comment + +// Case: NO_LINT for unknown check on line without errors. +class A7 { explicit A7(int i); }; // NOLINT(unknown-check) +// CHECK-MESSAGES: :[[@LINE-1]]:38: warning: unknown check name 'unknown-check' specified in NOLINT comment + +// Case: NO_LINT with not closed parenthesis without check names. +class A8 { A8(int i); }; // NOLINT( +// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: unbalanced parentheses in NOLINT comment; all diagnostics silenced for this line + +// Case: NO_LINT with not closed parenthesis with check names. +class A9 { A9(int i); }; // NOLINT(unknown-check +// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: unbalanced parentheses in NOLINT comment; all diagnostics silenced for this line + +// Case: NO_LINT with clang diagnostics +int f() { + int i = 0; // NOLINT +// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: there are no diagnostics on this line + int j = 0; // NOLINT(clang-diagnostic-unused-variable) +// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: there are no diagnostics for 'clang-diagnostic-unused-variable' on this line + return i + j; +} + +#define MACRO(X) class X { explicit X(int i); }; + +// Case: NO_LINT on macro instantiation line +MACRO(M1) // NOLINT +// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: there are no diagnostics on this line + +// Case: NO_LINT with the specific check on macro instantiation line +MACRO(M2) // NOLINT(google-explicit-constructor) +// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: there are no diagnostics for 'google-explicit-constructor' on this line + +// Case: NO_LINT on macro line +#define MACRO2(X) class X { explicit X(int i); }; // NOLINT +MACRO2(M3) +// CHECK-MESSAGES: :[[@LINE-2]]:54: warning: there are no diagnostics on this line + +// Case: NO_LINT with the specific check on macro line +#define MACRO3(X) class X { explicit X(int i); }; // NOLINT(google-explicit-constructor) +MACRO(M4) +// CHECK-MESSAGES: :[[@LINE-2]]:54: warning: there are no diagnostics for 'google-explicit-constructor' on this line + +// Case: NO_LINT with readability-nolint-usage on line without errors. +class B1 { explicit B1(int i); }; // NOLINT(readability-nolint-usage) + +// Case: NO_LINT with the specific check and readability-nolint-usage on line without errors. +class B2 { explicit B2(int i); }; // NOLINT(google-explicit-constructor, readability-nolint-usage) + +// Case: NO_LINT with the unknown check and readability-nolint-usage on line without errors. +class B3 { explicit B3(int i); }; // NOLINT(unknown-check, readability-nolint-usage) + + +// Case: NO_LINT_NEXT_LINE before line with an error. +// NOLINTNEXTLINE +class C1 { C1(int i); }; + +// Case: NO_LINT_NEXT_LINE before line without errors. +// NOLINTNEXTLINE +class C2 { explicit C2(int i); }; +// CHECK-MESSAGES: :[[@LINE-2]]:4: warning: there are no diagnostics on the next line + +// Case: NO_LINT_NEXT_LINE for the specific check before line with an error on it. +// NOLINTNEXTLINE(google-explicit-constructor) +class C3 { C3(int i); }; + +// Case: NO_LINT_NEXT_LINE for the specific check before line with an error on another check. +// NOLINTNEXTLINE(misc-unused-parameters) +class C4 { C4(int i); }; +// CHECK-MESSAGES: :[[@LINE-2]]:4: warning: there are no diagnostics for 'misc-unused-parameters' on the next line +// CHECK-MESSAGES: :[[@LINE-2]]:12: warning: single-argument constructors must be marked explicit + +// Case: NO_LINT_NEXT_LINE for the specific check before line without errors. +// NOLINTNEXTLINE(google-explicit-constructor) +class C5 { explicit C5(int i); }; +// CHECK-MESSAGES: :[[@LINE-2]]:4: warning: there are no diagnostics for 'google-explicit-constructor' on the next line + +// Case: NO_LINT_NEXT_LINE for unknown check before line with an error on some check. +// NOLINTNEXTLINE(unknown-check) +class C6 { C6(int i); }; +// CHECK-MESSAGES: :[[@LINE-2]]:4: warning: unknown check name 'unknown-check' specified in NOLINTNEXTLINE comment +// CHECK-MESSAGES: :[[@LINE-2]]:12: warning: single-argument constructors must be marked explicit + +// Case: NO_LINT_NEXT_LINE for unknown check before line without errors. +// NOLINTNEXTLINE(unknown-check) +class C7 { explicit C7(int i); }; +// CHECK-MESSAGES: :[[@LINE-2]]:4: warning: unknown check name 'unknown-check' specified in NOLINTNEXTLINE comment + +// Case: NO_LINT_NEXT_LINE with not closed parenthesis without check names. +// NOLINTNEXTLINE( +class C8 { C8(int i); }; +// CHECK-MESSAGES: :[[@LINE-2]]:4: warning: unbalanced parentheses in NOLINTNEXTLINE comment; all diagnostics silenced for this line + +// Case: NO_LINT_NEXT_LINE with not closed parenthesis with check names. +// NOLINTNEXTLINE(unknown-check +class C9 { C9(int i); }; +// CHECK-MESSAGES: :[[@LINE-2]]:4: warning: unbalanced parentheses in NOLINTNEXTLINE comment; all diagnostics silenced for this line + +// Case: NO_LINT_NEXT_LINE with clang diagnostics +int g() { + // NOLINTNEXTLINE + int i = 0; +// CHECK-MESSAGES: :[[@LINE-2]]:6: warning: there are no diagnostics on the next line + // NOLINTNEXTLINE(clang-diagnostic-unused-variable) + int j = 0; +// CHECK-MESSAGES: :[[@LINE-2]]:6: warning: there are no diagnostics for 'clang-diagnostic-unused-variable' on the next line + return i + j; +} + +#define MACRO(X) class X { explicit X(int i); }; + +// Case: NO_LINT_NEXT_LINE before macro instantiation line +// NOLINTNEXTLINE +MACRO(MM1) +// CHECK-MESSAGES: :[[@LINE-2]]:4: warning: there are no diagnostics on the next line + +// Case: NO_LINT_NEXT_LINE with the specific check before macro instantiation line +// NOLINTNEXTLINE(google-explicit-constructor) +MACRO(MM2) +// CHECK-MESSAGES: :[[@LINE-2]]:4: warning: there are no diagnostics for 'google-explicit-constructor' on the next line + +// Case: NO_LINT_NEXT_LINE before macro line +// NOLINTNEXTLINE +#define MACRO2(X) class X { explicit X(int i); }; +MACRO2(MM3) +// CHECK-MESSAGES: :[[@LINE-3]]:4: warning: there are no diagnostics on the next line + +// Case: NO_LINT_NEXT_LINE with the specific check before macro line +// NOLINTNEXTLINE(google-explicit-constructor) +#define MACRO3(X) class X { explicit X(int i); }; +MACRO(MM4) +// CHECK-MESSAGES: :[[@LINE-3]]:4: warning: there are no diagnostics for 'google-explicit-constructor' on the next line + +// Case: NO_LINT_NEXT_LINE with readability-nolint-usage before line without errors. +// NOLINTNEXTLINE(readability-nolint-usage) +// NOLINTNEXTLINE +class D1 { explicit D1(int i); }; + +// Case: NO_LINT_NEXT_LINE with the specific check and readability-nolint-usage before line without errors. +// NOLINTNEXTLINE(readability-nolint-usage) +// NOLINTNEXTLINE(google-explicit-constructor) +class D2 { explicit D2(int i); }; + +// Case: NO_LINT_NEXT_LINE with the unknown check and readability-nolint-usage before line without errors. +// NOLINTNEXTLINE(readability-nolint-usage) +// NOLINTNEXTLINE(unknown-check) +class D3 { explicit D3(int i); };