2525#include " llvm/ADT/StringSwitch.h"
2626#include < cassert>
2727#include < cstddef>
28- #include < iterator >
28+ #include < memory >
2929#include < optional>
3030#include < string>
31- #include < tuple>
3231#include < utility>
3332
3433namespace clang ::tidy {
@@ -79,7 +78,7 @@ class NoLintToken {
7978 // - An empty string means nothing is suppressed - equivalent to NOLINT().
8079 // - Negative globs ignored (which would effectively disable the suppression).
8180 NoLintToken (NoLintType Type, size_t Pos,
82- const std::optional<std::string > &Checks)
81+ const std::optional<StringRef > &Checks)
8382 : Type(Type), Pos(Pos), ChecksGlob(std::make_unique<CachedGlobList>(
8483 Checks.value_or(" *" ),
8584 /* KeepNegativeGlobs=*/ false )) {
@@ -93,15 +92,17 @@ class NoLintToken {
9392 // The location of the first character, "N", in "NOLINT".
9493 size_t Pos;
9594
95+ // A glob of the checks this NOLINT token disables.
96+ std::unique_ptr<CachedGlobList> ChecksGlob;
97+
9698 // If this NOLINT specifies checks, return the checks.
97- std::optional<std::string> checks () const { return Checks; }
99+ const std::optional<std::string> & checks () const { return Checks; }
98100
99101 // Whether this NOLINT applies to the provided check.
100102 bool suppresses (StringRef Check) const { return ChecksGlob->contains (Check); }
101103
102104private:
103105 std::optional<std::string> Checks;
104- std::unique_ptr<CachedGlobList> ChecksGlob;
105106};
106107
107108} // namespace
@@ -131,11 +132,11 @@ static SmallVector<NoLintToken> getNoLints(StringRef Buffer) {
131132 continue ;
132133
133134 // Get checks, if specified.
134- std::optional<std::string > Checks;
135+ std::optional<StringRef > Checks;
135136 if (Pos < Buffer.size () && Buffer[Pos] == ' (' ) {
136137 size_t ClosingBracket = Buffer.find_first_of (" \n )" , ++Pos);
137138 if (ClosingBracket != StringRef::npos && Buffer[ClosingBracket] == ' )' ) {
138- Checks = Buffer.slice (Pos, ClosingBracket). str () ;
139+ Checks = Buffer.slice (Pos, ClosingBracket);
139140 Pos = ClosingBracket + 1 ;
140141 }
141142 }
@@ -155,34 +156,51 @@ namespace {
155156// Represents a source range within a pair of NOLINT(BEGIN/END) comments.
156157class NoLintBlockToken {
157158public:
158- NoLintBlockToken (NoLintToken Begin, const NoLintToken &End)
159- : Begin(std::move(Begin)), EndPos(End.Pos) {
160- assert (this ->Begin .Type == NoLintType::NoLintBegin);
161- assert (End.Type == NoLintType::NoLintEnd);
162- assert (this ->Begin .Pos < End.Pos );
163- assert (this ->Begin .checks () == End.checks ());
164- }
159+ NoLintBlockToken (size_t BeginPos, size_t EndPos,
160+ std::unique_ptr<CachedGlobList> ChecksGlob)
161+ : BeginPos(BeginPos), EndPos(EndPos), ChecksGlob(std::move(ChecksGlob)) {}
165162
166163 // Whether the provided diagnostic is within and is suppressible by this block
167164 // of NOLINT(BEGIN/END) comments.
168165 bool suppresses (size_t DiagPos, StringRef DiagName) const {
169- return (Begin. Pos < DiagPos) && (DiagPos < EndPos) &&
170- Begin. suppresses (DiagName);
166+ return (BeginPos < DiagPos) && (DiagPos < EndPos) &&
167+ ChecksGlob-> contains (DiagName);
171168 }
172169
173170private:
174- NoLintToken Begin ;
171+ size_t BeginPos ;
175172 size_t EndPos;
173+ std::unique_ptr<CachedGlobList> ChecksGlob;
176174};
177175
178176} // namespace
179177
178+ // Construct a [clang-tidy-nolint] diagnostic to do with the unmatched
179+ // NOLINT(BEGIN/END) pair.
180+ static tooling::Diagnostic makeNoLintError (const SourceManager &SrcMgr,
181+ FileID File,
182+ const NoLintToken &NoLint) {
183+ tooling::Diagnostic Error;
184+ Error.DiagLevel = tooling::Diagnostic::Error;
185+ Error.DiagnosticName = " clang-tidy-nolint" ;
186+ StringRef Message =
187+ (NoLint.Type == NoLintType::NoLintBegin)
188+ ? (" unmatched 'NOLINTBEGIN' comment without a subsequent 'NOLINT"
189+ " END' comment" )
190+ : (" unmatched 'NOLINTEND' comment without a previous 'NOLINT"
191+ " BEGIN' comment" );
192+ SourceLocation Loc = SrcMgr.getComposedLoc (File, NoLint.Pos );
193+ Error.Message = tooling::DiagnosticMessage (Message, SrcMgr, Loc);
194+ return Error;
195+ }
196+
180197// Match NOLINTBEGINs with their corresponding NOLINTENDs and move them into
181- // `NoLintBlockToken`s. If any BEGINs or ENDs are left over, they are moved to
182- // `UnmatchedTokens `.
198+ // `NoLintBlockToken`s. If any BEGINs or ENDs are left over, a diagnostic is
199+ // written to `NoLintErrors `.
183200static SmallVector<NoLintBlockToken>
184- formNoLintBlocks (SmallVector<NoLintToken> NoLints,
185- SmallVectorImpl<NoLintToken> &UnmatchedTokens) {
201+ formNoLintBlocks (SmallVector<NoLintToken> NoLints, const SourceManager &SrcMgr,
202+ FileID File,
203+ SmallVectorImpl<tooling::Diagnostic> &NoLintErrors) {
186204 SmallVector<NoLintBlockToken> CompletedBlocks;
187205 SmallVector<NoLintToken> Stack;
188206
@@ -196,16 +214,20 @@ formNoLintBlocks(SmallVector<NoLintToken> NoLints,
196214 // A new block is being started. Add it to the stack.
197215 Stack.emplace_back (std::move (NoLint));
198216 else if (NoLint.Type == NoLintType::NoLintEnd) {
199- if (!Stack.empty () && Stack.back ().checks () == NoLint.checks ())
217+ if (!Stack.empty () && Stack.back ().checks () == NoLint.checks ()) {
200218 // The previous block is being closed. Pop one element off the stack.
201- CompletedBlocks.emplace_back (Stack.pop_back_val (), NoLint);
202- else
219+ CompletedBlocks.emplace_back (Stack.back ().Pos , NoLint.Pos ,
220+ std::move (Stack.back ().ChecksGlob ));
221+ Stack.pop_back ();
222+ } else
203223 // Trying to close the wrong block.
204- UnmatchedTokens .emplace_back (std::move ( NoLint));
224+ NoLintErrors .emplace_back (makeNoLintError (SrcMgr, File, NoLint));
205225 }
206226 }
207227
208- llvm::move (Stack, std::back_inserter (UnmatchedTokens));
228+ for (const NoLintToken &NoLint : Stack)
229+ NoLintErrors.emplace_back (makeNoLintError (SrcMgr, File, NoLint));
230+
209231 return CompletedBlocks;
210232}
211233
@@ -274,7 +296,7 @@ static std::pair<size_t, size_t> getLineStartAndEnd(StringRef Buffer,
274296 size_t From) {
275297 size_t StartPos = Buffer.find_last_of (' \n ' , From) + 1 ;
276298 size_t EndPos = std::min (Buffer.find (' \n ' , From), Buffer.size ());
277- return std::make_pair ( StartPos, EndPos) ;
299+ return { StartPos, EndPos} ;
278300}
279301
280302// Whether the line has a NOLINT of type = `Type` that can suppress the
@@ -317,9 +339,7 @@ bool NoLintDirectiveHandler::Impl::diagHasNoLint(
317339 SmallVectorImpl<tooling::Diagnostic> &NoLintErrors, bool AllowIO,
318340 bool EnableNoLintBlocks) {
319341 // Translate the diagnostic's SourceLocation to a raw file + offset pair.
320- FileID File;
321- unsigned int Pos = 0 ;
322- std::tie (File, Pos) = SrcMgr.getDecomposedSpellingLoc (DiagLoc);
342+ const auto [File, Pos] = SrcMgr.getDecomposedSpellingLoc (DiagLoc);
323343
324344 // We will only see NOLINTs in user-authored sources. No point reading the
325345 // file if it is a <built-in>.
@@ -349,46 +369,21 @@ bool NoLintDirectiveHandler::Impl::diagHasNoLint(
349369 return false ;
350370
351371 // Do we have cached NOLINT block locations for this file?
352- if (Cache.count (*FileName) == 0 )
372+ if (! Cache.contains (*FileName))
353373 // Warning: heavy operation - need to read entire file.
354374 generateCache (SrcMgr, *FileName, File, *Buffer, NoLintErrors);
355375
356376 return withinNoLintBlock (Cache[*FileName], Pos, DiagName);
357377}
358378
359- // Construct a [clang-tidy-nolint] diagnostic to do with the unmatched
360- // NOLINT(BEGIN/END) pair.
361- static tooling::Diagnostic makeNoLintError (const SourceManager &SrcMgr,
362- FileID File,
363- const NoLintToken &NoLint) {
364- tooling::Diagnostic Error;
365- Error.DiagLevel = tooling::Diagnostic::Error;
366- Error.DiagnosticName = " clang-tidy-nolint" ;
367- StringRef Message =
368- (NoLint.Type == NoLintType::NoLintBegin)
369- ? (" unmatched 'NOLINTBEGIN' comment without a subsequent 'NOLINT"
370- " END' comment" )
371- : (" unmatched 'NOLINTEND' comment without a previous 'NOLINT"
372- " BEGIN' comment" );
373- SourceLocation Loc = SrcMgr.getComposedLoc (File, NoLint.Pos );
374- Error.Message = tooling::DiagnosticMessage (Message, SrcMgr, Loc);
375- return Error;
376- }
377-
378379// Find all NOLINT(BEGIN/END) blocks in a file and store in the cache.
379380void NoLintDirectiveHandler::Impl::generateCache (
380381 const SourceManager &SrcMgr, StringRef FileName, FileID File,
381382 StringRef Buffer, SmallVectorImpl<tooling::Diagnostic> &NoLintErrors) {
382- // Read entire file to get all NOLINTs.
383- SmallVector<NoLintToken> NoLints = getNoLints (Buffer);
384-
385- // Match each BEGIN with its corresponding END.
386- SmallVector<NoLintToken> UnmatchedTokens;
387- Cache[FileName] = formNoLintBlocks (std::move (NoLints), UnmatchedTokens);
388-
389- // Raise error for any BEGIN/END left over.
390- for (const NoLintToken &NoLint : UnmatchedTokens)
391- NoLintErrors.emplace_back (makeNoLintError (SrcMgr, File, NoLint));
383+ // Read entire file to get all NOLINTs and match each BEGIN with its
384+ // corresponding END, raising errors for any BEGIN or END that is unmatched.
385+ Cache.try_emplace (FileName, formNoLintBlocks (getNoLints (Buffer), SrcMgr, File,
386+ NoLintErrors));
392387}
393388
394389// ===----------------------------------------------------------------------===//
0 commit comments