25
25
#include " llvm/ADT/StringSwitch.h"
26
26
#include < cassert>
27
27
#include < cstddef>
28
- #include < iterator >
28
+ #include < memory >
29
29
#include < optional>
30
30
#include < string>
31
- #include < tuple>
32
31
#include < utility>
33
32
34
33
namespace clang ::tidy {
@@ -79,7 +78,7 @@ class NoLintToken {
79
78
// - An empty string means nothing is suppressed - equivalent to NOLINT().
80
79
// - Negative globs ignored (which would effectively disable the suppression).
81
80
NoLintToken (NoLintType Type, size_t Pos,
82
- const std::optional<std::string > &Checks)
81
+ const std::optional<StringRef > &Checks)
83
82
: Type(Type), Pos(Pos), ChecksGlob(std::make_unique<CachedGlobList>(
84
83
Checks.value_or(" *" ),
85
84
/* KeepNegativeGlobs=*/ false )) {
@@ -93,15 +92,17 @@ class NoLintToken {
93
92
// The location of the first character, "N", in "NOLINT".
94
93
size_t Pos;
95
94
95
+ // A glob of the checks this NOLINT token disables.
96
+ std::unique_ptr<CachedGlobList> ChecksGlob;
97
+
96
98
// 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; }
98
100
99
101
// Whether this NOLINT applies to the provided check.
100
102
bool suppresses (StringRef Check) const { return ChecksGlob->contains (Check); }
101
103
102
104
private:
103
105
std::optional<std::string> Checks;
104
- std::unique_ptr<CachedGlobList> ChecksGlob;
105
106
};
106
107
107
108
} // namespace
@@ -131,11 +132,11 @@ static SmallVector<NoLintToken> getNoLints(StringRef Buffer) {
131
132
continue ;
132
133
133
134
// Get checks, if specified.
134
- std::optional<std::string > Checks;
135
+ std::optional<StringRef > Checks;
135
136
if (Pos < Buffer.size () && Buffer[Pos] == ' (' ) {
136
137
size_t ClosingBracket = Buffer.find_first_of (" \n )" , ++Pos);
137
138
if (ClosingBracket != StringRef::npos && Buffer[ClosingBracket] == ' )' ) {
138
- Checks = Buffer.slice (Pos, ClosingBracket). str () ;
139
+ Checks = Buffer.slice (Pos, ClosingBracket);
139
140
Pos = ClosingBracket + 1 ;
140
141
}
141
142
}
@@ -155,34 +156,51 @@ namespace {
155
156
// Represents a source range within a pair of NOLINT(BEGIN/END) comments.
156
157
class NoLintBlockToken {
157
158
public:
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)) {}
165
162
166
163
// Whether the provided diagnostic is within and is suppressible by this block
167
164
// of NOLINT(BEGIN/END) comments.
168
165
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);
171
168
}
172
169
173
170
private:
174
- NoLintToken Begin ;
171
+ size_t BeginPos ;
175
172
size_t EndPos;
173
+ std::unique_ptr<CachedGlobList> ChecksGlob;
176
174
};
177
175
178
176
} // namespace
179
177
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
+
180
197
// 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 `.
183
200
static 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) {
186
204
SmallVector<NoLintBlockToken> CompletedBlocks;
187
205
SmallVector<NoLintToken> Stack;
188
206
@@ -196,16 +214,20 @@ formNoLintBlocks(SmallVector<NoLintToken> NoLints,
196
214
// A new block is being started. Add it to the stack.
197
215
Stack.emplace_back (std::move (NoLint));
198
216
else if (NoLint.Type == NoLintType::NoLintEnd) {
199
- if (!Stack.empty () && Stack.back ().checks () == NoLint.checks ())
217
+ if (!Stack.empty () && Stack.back ().checks () == NoLint.checks ()) {
200
218
// 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
203
223
// Trying to close the wrong block.
204
- UnmatchedTokens .emplace_back (std::move ( NoLint));
224
+ NoLintErrors .emplace_back (makeNoLintError (SrcMgr, File, NoLint));
205
225
}
206
226
}
207
227
208
- llvm::move (Stack, std::back_inserter (UnmatchedTokens));
228
+ for (const NoLintToken &NoLint : Stack)
229
+ NoLintErrors.emplace_back (makeNoLintError (SrcMgr, File, NoLint));
230
+
209
231
return CompletedBlocks;
210
232
}
211
233
@@ -274,7 +296,7 @@ static std::pair<size_t, size_t> getLineStartAndEnd(StringRef Buffer,
274
296
size_t From) {
275
297
size_t StartPos = Buffer.find_last_of (' \n ' , From) + 1 ;
276
298
size_t EndPos = std::min (Buffer.find (' \n ' , From), Buffer.size ());
277
- return std::make_pair ( StartPos, EndPos) ;
299
+ return { StartPos, EndPos} ;
278
300
}
279
301
280
302
// Whether the line has a NOLINT of type = `Type` that can suppress the
@@ -317,9 +339,7 @@ bool NoLintDirectiveHandler::Impl::diagHasNoLint(
317
339
SmallVectorImpl<tooling::Diagnostic> &NoLintErrors, bool AllowIO,
318
340
bool EnableNoLintBlocks) {
319
341
// 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);
323
343
324
344
// We will only see NOLINTs in user-authored sources. No point reading the
325
345
// file if it is a <built-in>.
@@ -349,46 +369,21 @@ bool NoLintDirectiveHandler::Impl::diagHasNoLint(
349
369
return false ;
350
370
351
371
// Do we have cached NOLINT block locations for this file?
352
- if (Cache.count (*FileName) == 0 )
372
+ if (! Cache.contains (*FileName))
353
373
// Warning: heavy operation - need to read entire file.
354
374
generateCache (SrcMgr, *FileName, File, *Buffer, NoLintErrors);
355
375
356
376
return withinNoLintBlock (Cache[*FileName], Pos, DiagName);
357
377
}
358
378
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
-
378
379
// Find all NOLINT(BEGIN/END) blocks in a file and store in the cache.
379
380
void NoLintDirectiveHandler::Impl::generateCache (
380
381
const SourceManager &SrcMgr, StringRef FileName, FileID File,
381
382
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));
392
387
}
393
388
394
389
// ===----------------------------------------------------------------------===//
0 commit comments