17 #include "clang/Basic/LLVM.h"
18 #include "clang/Basic/SourceLocation.h"
19 #include "clang/Basic/SourceManager.h"
20 #include "clang/Tooling/Core/Diagnostic.h"
21 #include "llvm/ADT/ArrayRef.h"
22 #include "llvm/ADT/None.h"
23 #include "llvm/ADT/Optional.h"
24 #include "llvm/ADT/STLExtras.h"
25 #include "llvm/ADT/SmallVector.h"
26 #include "llvm/ADT/StringExtras.h"
27 #include "llvm/ADT/StringMap.h"
28 #include "llvm/ADT/StringSwitch.h"
34 #include <type_traits>
50 auto Type = llvm::StringSwitch<Optional<NoLintType>>(Str)
67 SmallVector<StringRef> Split;
69 for (StringRef &Check : Split)
86 :
Type(
Type),
Pos(
Pos), ChecksGlob(std::make_unique<CachedGlobList>(
100 Optional<std::string> checks()
const {
return Checks; }
103 bool suppresses(StringRef Check)
const {
return ChecksGlob->contains(Check); }
106 Optional<std::string>
Checks;
107 std::unique_ptr<CachedGlobList> ChecksGlob;
113 static SmallVector<NoLintToken>
getNoLints(StringRef Buffer) {
114 static constexpr llvm::StringLiteral NOLINT =
"NOLINT";
115 SmallVector<NoLintToken> NoLints;
118 while (
Pos < Buffer.size()) {
120 const size_t NoLintPos = Buffer.find(NOLINT,
Pos);
121 if (NoLintPos == StringRef::npos)
126 Pos = NoLintPos + NOLINT.size();
127 while (
Pos < Buffer.size() && llvm::isAlpha(Buffer[
Pos]))
137 Optional<std::string>
Checks;
138 if (
Pos < Buffer.size() && Buffer[
Pos] ==
'(') {
139 size_t ClosingBracket = Buffer.find_first_of(
"\n)", ++
Pos);
140 if (ClosingBracket != StringRef::npos && Buffer[ClosingBracket] ==
')') {
141 Checks = Buffer.slice(
Pos, ClosingBracket).str();
142 Pos = ClosingBracket + 1;
159 class NoLintBlockToken {
161 NoLintBlockToken(NoLintToken Begin,
const NoLintToken &End)
162 : Begin(std::move(Begin)), EndPos(End.
Pos) {
165 assert(this->Begin.Pos < End.Pos);
166 assert(this->Begin.checks() == End.checks());
171 bool suppresses(
size_t DiagPos, StringRef DiagName)
const {
172 return (Begin.Pos < DiagPos) && (DiagPos < EndPos) &&
173 Begin.suppresses(DiagName);
186 static SmallVector<NoLintBlockToken>
188 SmallVectorImpl<NoLintToken> &UnmatchedTokens) {
189 SmallVector<NoLintBlockToken> CompletedBlocks;
190 SmallVector<NoLintToken> Stack;
197 for (NoLintToken &
NoLint : NoLints) {
200 Stack.emplace_back(std::move(
NoLint));
202 if (!Stack.empty() && Stack.back().checks() ==
NoLint.checks())
204 CompletedBlocks.emplace_back(Stack.pop_back_val(),
NoLint);
207 UnmatchedTokens.emplace_back(std::move(
NoLint));
211 llvm::move(Stack, std::back_inserter(UnmatchedTokens));
212 return CompletedBlocks;
223 SmallVectorImpl<tooling::Diagnostic> &NoLintErrors,
224 bool AllowIO,
bool EnableNoLintBlocks);
227 bool diagHasNoLintInMacro(
const Diagnostic &Diag, StringRef DiagName,
228 SmallVectorImpl<tooling::Diagnostic> &NoLintErrors,
229 bool AllowIO,
bool EnableNoLintBlocks);
231 bool diagHasNoLint(StringRef DiagName, SourceLocation DiagLoc,
232 const SourceManager &SrcMgr,
233 SmallVectorImpl<tooling::Diagnostic> &NoLintErrors,
234 bool AllowIO,
bool EnableNoLintBlocks);
236 void generateCache(
const SourceManager &SrcMgr, StringRef
FileName,
237 FileID File, StringRef Buffer,
238 SmallVectorImpl<tooling::Diagnostic> &NoLintErrors);
240 llvm::StringMap<SmallVector<NoLintBlockToken>> Cache;
244 DiagnosticsEngine::Level DiagLevel,
const Diagnostic &Diag,
245 StringRef DiagName, SmallVectorImpl<tooling::Diagnostic> &NoLintErrors,
246 bool AllowIO,
bool EnableNoLintBlocks) {
249 return diagHasNoLintInMacro(Diag, DiagName, NoLintErrors, AllowIO,
255 bool NoLintDirectiveHandler::Impl::diagHasNoLintInMacro(
257 SmallVectorImpl<tooling::Diagnostic> &NoLintErrors,
bool AllowIO,
258 bool EnableNoLintBlocks) {
259 SourceLocation DiagLoc = Diag.getLocation();
260 if (DiagLoc.isInvalid())
262 const SourceManager &SrcMgr = Diag.getSourceManager();
264 if (diagHasNoLint(DiagName, DiagLoc, SrcMgr, NoLintErrors, AllowIO,
267 if (!DiagLoc.isMacroID())
269 DiagLoc = SrcMgr.getImmediateMacroCallerLoc(DiagLoc);
278 size_t StartPos = Buffer.find_last_of(
'\n', From) + 1;
279 size_t EndPos = std::min(Buffer.find(
'\n', From), Buffer.size());
280 return std::make_pair(StartPos, EndPos);
286 std::pair<size_t, size_t> LineStartAndEnd,
289 Buffer = Buffer.slice(LineStartAndEnd.first, LineStartAndEnd.second);
290 SmallVector<NoLintToken> NoLints =
getNoLints(Buffer);
293 return llvm::any_of(NoLints, [&](
const NoLintToken &
NoLint) {
301 size_t DiagPos, StringRef DiagName) {
302 return llvm::any_of(NoLintBlocks, [&](
const NoLintBlockToken &NoLintBlock) {
303 return NoLintBlock.suppresses(DiagPos, DiagName);
308 static Optional<StringRef>
getBuffer(
const SourceManager &SrcMgr, FileID File,
310 return AllowIO ? SrcMgr.getBufferDataOrNone(File)
311 : SrcMgr.getBufferDataIfLoaded(File);
318 bool NoLintDirectiveHandler::Impl::diagHasNoLint(
319 StringRef DiagName, SourceLocation DiagLoc,
const SourceManager &SrcMgr,
320 SmallVectorImpl<tooling::Diagnostic> &NoLintErrors,
bool AllowIO,
321 bool EnableNoLintBlocks) {
324 unsigned int Pos = 0;
325 std::tie(File,
Pos) = SrcMgr.getDecomposedSpellingLoc(DiagLoc);
329 Optional<StringRef>
FileName = SrcMgr.getNonBuiltinFilenameForID(File);
334 Optional<StringRef> Buffer =
getBuffer(SrcMgr, File, AllowIO);
344 if (ThisLine.first > 0) {
351 if (!EnableNoLintBlocks)
357 generateCache(SrcMgr, *
FileName, File, *Buffer, NoLintErrors);
366 const NoLintToken &NoLint) {
369 Error.DiagnosticName =
"clang-tidy-nolint";
372 ? (
"unmatched 'NOLINTBEGIN' comment without a subsequent 'NOLINT"
374 : (
"unmatched 'NOLINTEND' comment without a previous 'NOLINT"
376 SourceLocation
Loc = SrcMgr.getComposedLoc(File,
NoLint.Pos);
377 Error.Message = tooling::DiagnosticMessage(
Message, SrcMgr,
Loc);
382 void NoLintDirectiveHandler::Impl::generateCache(
383 const SourceManager &SrcMgr, StringRef
FileName, FileID File,
384 StringRef Buffer, SmallVectorImpl<tooling::Diagnostic> &NoLintErrors) {
386 SmallVector<NoLintToken> NoLints =
getNoLints(Buffer);
389 SmallVector<NoLintToken> UnmatchedTokens;
393 for (
const NoLintToken &NoLint : UnmatchedTokens)
402 : PImpl(std::make_unique<
Impl>()) {}
407 DiagnosticsEngine::Level DiagLevel,
const Diagnostic &Diag,
408 StringRef DiagName, SmallVectorImpl<tooling::Diagnostic> &NoLintErrors,
409 bool AllowIO,
bool EnableNoLintBlocks) {
410 return PImpl->shouldSuppress(DiagLevel, Diag, DiagName, NoLintErrors, AllowIO,