18
18
#include " ClangTidyDiagnosticConsumer.h"
19
19
#include " ClangTidyOptions.h"
20
20
#include " GlobList.h"
21
+ #include " NoLintDirectiveHandler.h"
21
22
#include " clang/AST/ASTContext.h"
22
23
#include " clang/AST/ASTDiagnostic.h"
23
24
#include " clang/AST/Attr.h"
@@ -188,7 +189,7 @@ DiagnosticBuilder ClangTidyContext::diag(
188
189
return DiagEngine->Report (ID);
189
190
}
190
191
191
- DiagnosticBuilder ClangTidyContext::diag (const ClangTidyError &Error) {
192
+ DiagnosticBuilder ClangTidyContext::diag (const tooling::Diagnostic &Error) {
192
193
SourceManager &SM = DiagEngine->getSourceManager ();
193
194
llvm::ErrorOr<const FileEntry *> File =
194
195
SM.getFileManager ().getFile (Error.Message .FilePath );
@@ -206,6 +207,15 @@ DiagnosticBuilder ClangTidyContext::configurationDiag(
206
207
return diag (" clang-tidy-config" , Message, Level);
207
208
}
208
209
210
+ bool ClangTidyContext::shouldSuppressDiagnostic (
211
+ DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info,
212
+ SmallVectorImpl<tooling::Diagnostic> &NoLintErrors, bool AllowIO,
213
+ bool EnableNoLintBlocks) {
214
+ std::string CheckName = getCheckName (Info.getID ());
215
+ return NoLintHandler.shouldSuppress (DiagLevel, Info, CheckName, NoLintErrors,
216
+ AllowIO, EnableNoLintBlocks);
217
+ }
218
+
209
219
void ClangTidyContext::setSourceManager (SourceManager *SourceMgr) {
210
220
DiagEngine->setSourceManager (SourceMgr);
211
221
}
@@ -307,218 +317,9 @@ void ClangTidyDiagnosticConsumer::finalizeLastError() {
307
317
LastErrorPassesLineFilter = false ;
308
318
}
309
319
310
- static bool isNOLINTFound (StringRef NolintDirectiveText, StringRef CheckName,
311
- StringRef Line, size_t *FoundNolintIndex = nullptr ,
312
- StringRef *FoundNolintChecksStr = nullptr ) {
313
- if (FoundNolintIndex)
314
- *FoundNolintIndex = StringRef::npos;
315
- if (FoundNolintChecksStr)
316
- *FoundNolintChecksStr = StringRef ();
317
-
318
- size_t NolintIndex = Line.find (NolintDirectiveText);
319
- if (NolintIndex == StringRef::npos)
320
- return false ;
321
-
322
- size_t BracketIndex = NolintIndex + NolintDirectiveText.size ();
323
- if (BracketIndex < Line.size () && isalnum (Line[BracketIndex])) {
324
- // Reject this search result, otherwise it will cause false positives when
325
- // NOLINT is found as a substring of NOLINT(NEXTLINE/BEGIN/END).
326
- return false ;
327
- }
328
-
329
- // Check if specific checks are specified in brackets.
330
- if (BracketIndex < Line.size () && Line[BracketIndex] == ' (' ) {
331
- ++BracketIndex;
332
- const size_t BracketEndIndex = Line.find (' )' , BracketIndex);
333
- if (BracketEndIndex != StringRef::npos) {
334
- StringRef ChecksStr =
335
- Line.substr (BracketIndex, BracketEndIndex - BracketIndex);
336
- if (FoundNolintChecksStr)
337
- *FoundNolintChecksStr = ChecksStr;
338
- // Allow specifying a few checks with a glob expression, ignoring
339
- // negative globs (which would effectively disable the suppression).
340
- GlobList Globs (ChecksStr, /* KeepNegativeGlobs=*/ false );
341
- if (!Globs.contains (CheckName))
342
- return false ;
343
- }
344
- }
345
-
346
- if (FoundNolintIndex)
347
- *FoundNolintIndex = NolintIndex;
348
-
349
- return true ;
350
- }
351
-
352
- static llvm::Optional<StringRef> getBuffer (const SourceManager &SM, FileID File,
353
- bool AllowIO) {
354
- return AllowIO ? SM.getBufferDataOrNone (File)
355
- : SM.getBufferDataIfLoaded (File);
356
- }
357
-
358
- static ClangTidyError createNolintError (const ClangTidyContext &Context,
359
- const SourceManager &SM,
360
- SourceLocation Loc,
361
- bool IsNolintBegin) {
362
- ClangTidyError Error (" clang-tidy-nolint" , ClangTidyError::Error,
363
- Context.getCurrentBuildDirectory (), false );
364
- StringRef Message =
365
- IsNolintBegin
366
- ? (" unmatched 'NOLINTBEGIN' comment without a subsequent 'NOLINT"
367
- " END' comment" )
368
- : (" unmatched 'NOLINTEND' comment without a previous 'NOLINT"
369
- " BEGIN' comment" );
370
- Error.Message = tooling::DiagnosticMessage (Message, SM, Loc);
371
- return Error;
372
- }
373
-
374
- static Optional<ClangTidyError> tallyNolintBegins (
375
- const ClangTidyContext &Context, const SourceManager &SM,
376
- StringRef CheckName, SmallVector<StringRef> Lines, SourceLocation LinesLoc,
377
- SmallVector<std::pair<SourceLocation, StringRef>> &NolintBegins) {
378
- // Keep a running total of how many NOLINT(BEGIN...END) blocks are active, as
379
- // well as the bracket expression (if any) that was used in the NOLINT
380
- // expression.
381
- size_t NolintIndex;
382
- StringRef NolintChecksStr;
383
- for (const auto &Line : Lines) {
384
- if (isNOLINTFound (" NOLINTBEGIN" , CheckName, Line, &NolintIndex,
385
- &NolintChecksStr)) {
386
- // Check if a new block is being started.
387
- NolintBegins.emplace_back (std::make_pair (
388
- LinesLoc.getLocWithOffset (NolintIndex), NolintChecksStr));
389
- } else if (isNOLINTFound (" NOLINTEND" , CheckName, Line, &NolintIndex,
390
- &NolintChecksStr)) {
391
- // Check if the previous block is being closed.
392
- if (!NolintBegins.empty () &&
393
- NolintBegins.back ().second == NolintChecksStr) {
394
- NolintBegins.pop_back ();
395
- } else {
396
- // Trying to close a nonexistent block. Return a diagnostic about this
397
- // misuse that can be displayed along with the original clang-tidy check
398
- // that the user was attempting to suppress.
399
- return createNolintError (Context, SM,
400
- LinesLoc.getLocWithOffset (NolintIndex), false );
401
- }
402
- }
403
- // Advance source location to the next line.
404
- LinesLoc = LinesLoc.getLocWithOffset (Line.size () + sizeof (' \n ' ));
405
- }
406
- return None; // All NOLINT(BEGIN/END) use has been consistent so far.
407
- }
408
-
409
- static bool
410
- lineIsWithinNolintBegin (const ClangTidyContext &Context,
411
- SmallVectorImpl<ClangTidyError> &SuppressionErrors,
412
- const SourceManager &SM, SourceLocation Loc,
413
- StringRef CheckName, StringRef TextBeforeDiag,
414
- StringRef TextAfterDiag) {
415
- Loc = SM.getExpansionRange (Loc).getBegin ();
416
- SourceLocation FileStartLoc = SM.getLocForStartOfFile (SM.getFileID (Loc));
417
- SmallVector<std::pair<SourceLocation, StringRef>> NolintBegins;
418
-
419
- // Check if there's an open NOLINT(BEGIN...END) block on the previous lines.
420
- SmallVector<StringRef> PrevLines;
421
- TextBeforeDiag.split (PrevLines, ' \n ' );
422
- auto Error = tallyNolintBegins (Context, SM, CheckName, PrevLines,
423
- FileStartLoc, NolintBegins);
424
- if (Error) {
425
- SuppressionErrors.emplace_back (Error.getValue ());
426
- }
427
- bool WithinNolintBegin = !NolintBegins.empty ();
428
-
429
- // Check that every block is terminated correctly on the following lines.
430
- SmallVector<StringRef> FollowingLines;
431
- TextAfterDiag.split (FollowingLines, ' \n ' );
432
- Error = tallyNolintBegins (Context, SM, CheckName, FollowingLines, Loc,
433
- NolintBegins);
434
- if (Error) {
435
- SuppressionErrors.emplace_back (Error.getValue ());
436
- }
437
-
438
- // The following blocks were never closed. Return diagnostics for each
439
- // instance that can be displayed along with the original clang-tidy check
440
- // that the user was attempting to suppress.
441
- for (const auto &NolintBegin : NolintBegins) {
442
- SuppressionErrors.emplace_back (
443
- createNolintError (Context, SM, NolintBegin.first , true ));
444
- }
445
-
446
- return WithinNolintBegin && SuppressionErrors.empty ();
447
- }
448
-
449
- static bool
450
- lineIsMarkedWithNOLINT (const ClangTidyContext &Context,
451
- SmallVectorImpl<ClangTidyError> &SuppressionErrors,
452
- bool AllowIO, const SourceManager &SM,
453
- SourceLocation Loc, StringRef CheckName,
454
- bool EnableNolintBlocks) {
455
- // Get source code for this location.
456
- FileID File;
457
- unsigned Offset;
458
- std::tie (File, Offset) = SM.getDecomposedSpellingLoc (Loc);
459
- Optional<StringRef> Buffer = getBuffer (SM, File, AllowIO);
460
- if (!Buffer)
461
- return false ;
462
-
463
- // Check if there's a NOLINT on this line.
464
- StringRef TextAfterDiag = Buffer->substr (Offset);
465
- StringRef RestOfThisLine, FollowingLines;
466
- std::tie (RestOfThisLine, FollowingLines) = TextAfterDiag.split (' \n ' );
467
- if (isNOLINTFound (" NOLINT" , CheckName, RestOfThisLine))
468
- return true ;
469
-
470
- // Check if there's a NOLINTNEXTLINE on the previous line.
471
- StringRef TextBeforeDiag = Buffer->substr (0 , Offset);
472
- size_t LastNewLinePos = TextBeforeDiag.rfind (' \n ' );
473
- StringRef PrevLines = (LastNewLinePos == StringRef::npos)
474
- ? StringRef ()
475
- : TextBeforeDiag.slice (0 , LastNewLinePos);
476
- LastNewLinePos = PrevLines.rfind (' \n ' );
477
- StringRef PrevLine = (LastNewLinePos == StringRef::npos)
478
- ? PrevLines
479
- : PrevLines.substr (LastNewLinePos + 1 );
480
- if (isNOLINTFound (" NOLINTNEXTLINE" , CheckName, PrevLine))
481
- return true ;
482
-
483
- // Check if this line is within a NOLINT(BEGIN...END) block.
484
- return EnableNolintBlocks &&
485
- lineIsWithinNolintBegin (Context, SuppressionErrors, SM, Loc, CheckName,
486
- TextBeforeDiag, TextAfterDiag);
487
- }
488
-
489
- static bool lineIsMarkedWithNOLINTinMacro (
490
- const Diagnostic &Info, const ClangTidyContext &Context,
491
- SmallVectorImpl<ClangTidyError> &SuppressionErrors, bool AllowIO,
492
- bool EnableNolintBlocks) {
493
- const SourceManager &SM = Info.getSourceManager ();
494
- SourceLocation Loc = Info.getLocation ();
495
- std::string CheckName = Context.getCheckName (Info.getID ());
496
- while (true ) {
497
- if (lineIsMarkedWithNOLINT (Context, SuppressionErrors, AllowIO, SM, Loc,
498
- CheckName, EnableNolintBlocks))
499
- return true ;
500
- if (!Loc.isMacroID ())
501
- return false ;
502
- Loc = SM.getImmediateExpansionRange (Loc).getBegin ();
503
- }
504
- return false ;
505
- }
506
-
507
320
namespace clang {
508
321
namespace tidy {
509
322
510
- bool shouldSuppressDiagnostic (
511
- DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info,
512
- ClangTidyContext &Context,
513
- SmallVectorImpl<ClangTidyError> &SuppressionErrors, bool AllowIO,
514
- bool EnableNolintBlocks) {
515
- return Info.getLocation ().isValid () &&
516
- DiagLevel != DiagnosticsEngine::Error &&
517
- DiagLevel != DiagnosticsEngine::Fatal &&
518
- lineIsMarkedWithNOLINTinMacro (Info, Context, SuppressionErrors,
519
- AllowIO, EnableNolintBlocks);
520
- }
521
-
522
323
const llvm::StringMap<tooling::Replacements> *
523
324
getFixIt (const tooling::Diagnostic &Diagnostic, bool GetFixFromNotes) {
524
325
if (!Diagnostic.Message .Fix .empty ())
@@ -545,12 +346,15 @@ void ClangTidyDiagnosticConsumer::HandleDiagnostic(
545
346
if (LastErrorWasIgnored && DiagLevel == DiagnosticsEngine::Note)
546
347
return ;
547
348
548
- SmallVector<ClangTidyError , 1 > SuppressionErrors;
549
- if (shouldSuppressDiagnostic (DiagLevel, Info, Context , SuppressionErrors,
550
- EnableNolintBlocks)) {
349
+ SmallVector<tooling::Diagnostic , 1 > SuppressionErrors;
350
+ if (Context. shouldSuppressDiagnostic (DiagLevel, Info, SuppressionErrors,
351
+ EnableNolintBlocks)) {
551
352
++Context.Stats .ErrorsIgnoredNOLINT ;
552
353
// Ignored a warning, should ignore related notes as well
553
354
LastErrorWasIgnored = true ;
355
+ Context.DiagEngine ->Clear ();
356
+ for (const auto &Error : SuppressionErrors)
357
+ Context.diag (Error);
554
358
return ;
555
359
}
556
360
0 commit comments