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/STLExtras.h"
23#include "llvm/ADT/SmallVector.h"
24#include "llvm/ADT/StringExtras.h"
25#include "llvm/ADT/StringMap.h"
26#include "llvm/ADT/StringSwitch.h"
48 auto Type = llvm::StringSwitch<std::optional<NoLintType>>(Str)
53 .Default(std::nullopt);
65 SmallVector<StringRef> Split;
66 Checks.split(Split,
',');
67 for (StringRef &Check : Split)
69 return llvm::join(Split,
",");
84 const std::optional<std::string> &Checks)
85 :
Type(
Type),
Pos(
Pos), ChecksGlob(std::make_unique<CachedGlobList>(
99 std::optional<std::string> checks()
const {
return Checks; }
102 bool suppresses(StringRef Check)
const {
return ChecksGlob->contains(Check); }
105 std::optional<std::string> Checks;
106 std::unique_ptr<CachedGlobList> ChecksGlob;
112static SmallVector<NoLintToken>
getNoLints(StringRef Buffer) {
113 static constexpr llvm::StringLiteral NOLINT =
"NOLINT";
114 SmallVector<NoLintToken> NoLints;
117 while (
Pos < Buffer.size()) {
119 const size_t NoLintPos = Buffer.find(NOLINT,
Pos);
120 if (NoLintPos == StringRef::npos)
125 Pos = NoLintPos + NOLINT.size();
126 while (
Pos < Buffer.size() && llvm::isAlpha(Buffer[
Pos]))
136 std::optional<std::string> Checks;
137 if (
Pos < Buffer.size() && Buffer[
Pos] ==
'(') {
138 size_t ClosingBracket = Buffer.find_first_of(
"\n)", ++
Pos);
139 if (ClosingBracket != StringRef::npos && Buffer[ClosingBracket] ==
')') {
140 Checks = Buffer.slice(
Pos, ClosingBracket).str();
141 Pos = ClosingBracket + 1;
145 NoLints.emplace_back(*
NoLintType, NoLintPos, Checks);
158class NoLintBlockToken {
160 NoLintBlockToken(NoLintToken Begin,
const NoLintToken &End)
161 : Begin(std::move(Begin)), EndPos(End.
Pos) {
164 assert(this->Begin.Pos < End.Pos);
165 assert(this->Begin.checks() == End.checks());
170 bool suppresses(
size_t DiagPos, StringRef DiagName)
const {
171 return (Begin.Pos < DiagPos) && (DiagPos < EndPos) &&
172 Begin.suppresses(DiagName);
185static SmallVector<NoLintBlockToken>
187 SmallVectorImpl<NoLintToken> &UnmatchedTokens) {
188 SmallVector<NoLintBlockToken> CompletedBlocks;
189 SmallVector<NoLintToken> Stack;
196 for (NoLintToken &
NoLint : NoLints) {
199 Stack.emplace_back(std::move(
NoLint));
201 if (!Stack.empty() && Stack.back().checks() ==
NoLint.checks())
203 CompletedBlocks.emplace_back(Stack.pop_back_val(),
NoLint);
206 UnmatchedTokens.emplace_back(std::move(
NoLint));
210 llvm::move(Stack, std::back_inserter(UnmatchedTokens));
211 return CompletedBlocks;
222 SmallVectorImpl<tooling::Diagnostic> &NoLintErrors,
223 bool AllowIO,
bool EnableNoLintBlocks);
226 bool diagHasNoLintInMacro(
const Diagnostic &Diag, StringRef DiagName,
227 SmallVectorImpl<tooling::Diagnostic> &NoLintErrors,
228 bool AllowIO,
bool EnableNoLintBlocks);
230 bool diagHasNoLint(StringRef DiagName, SourceLocation DiagLoc,
231 const SourceManager &SrcMgr,
232 SmallVectorImpl<tooling::Diagnostic> &NoLintErrors,
233 bool AllowIO,
bool EnableNoLintBlocks);
235 void generateCache(
const SourceManager &SrcMgr, StringRef
FileName,
236 FileID File, StringRef Buffer,
237 SmallVectorImpl<tooling::Diagnostic> &NoLintErrors);
239 llvm::StringMap<SmallVector<NoLintBlockToken>> Cache;
243 DiagnosticsEngine::Level DiagLevel,
const Diagnostic &Diag,
244 StringRef DiagName, SmallVectorImpl<tooling::Diagnostic> &NoLintErrors,
245 bool AllowIO,
bool EnableNoLintBlocks) {
246 if (DiagLevel >= DiagnosticsEngine::Error)
248 return diagHasNoLintInMacro(Diag, DiagName, NoLintErrors, AllowIO,
254bool NoLintDirectiveHandler::Impl::diagHasNoLintInMacro(
256 SmallVectorImpl<tooling::Diagnostic> &NoLintErrors,
bool AllowIO,
257 bool EnableNoLintBlocks) {
258 SourceLocation DiagLoc = Diag.getLocation();
259 if (DiagLoc.isInvalid())
261 const SourceManager &SrcMgr = Diag.getSourceManager();
263 if (diagHasNoLint(DiagName, DiagLoc, SrcMgr, NoLintErrors, AllowIO,
266 if (!DiagLoc.isMacroID())
268 DiagLoc = SrcMgr.getImmediateExpansionRange(DiagLoc).getBegin();
277 size_t StartPos = Buffer.find_last_of(
'\n', From) + 1;
278 size_t EndPos = std::min(Buffer.find(
'\n', From), Buffer.size());
279 return std::make_pair(StartPos, EndPos);
285 std::pair<size_t, size_t> LineStartAndEnd,
288 Buffer = Buffer.slice(LineStartAndEnd.first, LineStartAndEnd.second);
289 SmallVector<NoLintToken> NoLints =
getNoLints(Buffer);
292 return llvm::any_of(NoLints, [&](
const NoLintToken &
NoLint) {
300 size_t DiagPos, StringRef DiagName) {
301 return llvm::any_of(NoLintBlocks, [&](
const NoLintBlockToken &NoLintBlock) {
302 return NoLintBlock.suppresses(DiagPos, DiagName);
307static std::optional<StringRef>
getBuffer(
const SourceManager &SrcMgr,
308 FileID File,
bool AllowIO) {
309 return AllowIO ? SrcMgr.getBufferDataOrNone(File)
310 : SrcMgr.getBufferDataIfLoaded(File);
317bool NoLintDirectiveHandler::Impl::diagHasNoLint(
318 StringRef DiagName, SourceLocation DiagLoc,
const SourceManager &SrcMgr,
319 SmallVectorImpl<tooling::Diagnostic> &NoLintErrors,
bool AllowIO,
320 bool EnableNoLintBlocks) {
323 unsigned int Pos = 0;
324 std::tie(File,
Pos) = SrcMgr.getDecomposedSpellingLoc(DiagLoc);
328 std::optional<StringRef>
FileName = SrcMgr.getNonBuiltinFilenameForID(File);
333 std::optional<StringRef> Buffer =
getBuffer(SrcMgr, File, AllowIO);
343 if (ThisLine.first > 0) {
350 if (!EnableNoLintBlocks)
356 generateCache(SrcMgr, *
FileName, File, *Buffer, NoLintErrors);
365 const NoLintToken &
NoLint) {
366 tooling::Diagnostic
Error;
367 Error.DiagLevel = tooling::Diagnostic::Error;
368 Error.DiagnosticName =
"clang-tidy-nolint";
371 ? (
"unmatched 'NOLINTBEGIN' comment without a subsequent 'NOLINT"
373 : (
"unmatched 'NOLINTEND' comment without a previous 'NOLINT"
375 SourceLocation
Loc = SrcMgr.getComposedLoc(File,
NoLint.Pos);
376 Error.Message = tooling::DiagnosticMessage(Message, SrcMgr,
Loc);
381void NoLintDirectiveHandler::Impl::generateCache(
382 const SourceManager &SrcMgr, StringRef
FileName, FileID File,
383 StringRef Buffer, SmallVectorImpl<tooling::Diagnostic> &NoLintErrors) {
385 SmallVector<NoLintToken> NoLints =
getNoLints(Buffer);
388 SmallVector<NoLintToken> UnmatchedTokens;
392 for (
const NoLintToken &
NoLint : UnmatchedTokens)
401 : PImpl(std::make_unique<
Impl>()) {}
406 DiagnosticsEngine::Level DiagLevel,
const Diagnostic &Diag,
407 StringRef DiagName, SmallVectorImpl<tooling::Diagnostic> &NoLintErrors,
408 bool AllowIO,
bool EnableNoLintBlocks) {
409 return PImpl->shouldSuppress(DiagLevel, Diag, DiagName, NoLintErrors, AllowIO,
static constexpr llvm::SourceMgr::DiagKind Error
DiagnosticCallback Diagnostic
bool shouldSuppress(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Diag, StringRef DiagName, SmallVectorImpl< tooling::Diagnostic > &NoLintErrors, bool AllowIO, bool EnableNoLintBlocks)
~NoLintDirectiveHandler()
bool shouldSuppress(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Diag, llvm::StringRef DiagName, llvm::SmallVectorImpl< tooling::Diagnostic > &NoLintErrors, bool AllowIO, bool EnableNoLintBlocks)
static std::optional< StringRef > getBuffer(const SourceManager &SrcMgr, FileID File, bool AllowIO)
static std::pair< size_t, size_t > getLineStartAndEnd(StringRef Buffer, size_t From)
static tooling::Diagnostic makeNoLintError(const SourceManager &SrcMgr, FileID File, const NoLintToken &NoLint)
static SmallVector< NoLintToken > getNoLints(StringRef Buffer)
static std::optional< NoLintType > strToNoLintType(StringRef Str)
static SmallVector< NoLintBlockToken > formNoLintBlocks(SmallVector< NoLintToken > NoLints, SmallVectorImpl< NoLintToken > &UnmatchedTokens)
static std::string trimWhitespace(StringRef Checks)
static bool withinNoLintBlock(ArrayRef< NoLintBlockToken > NoLintBlocks, size_t DiagPos, StringRef DiagName)
static bool lineHasNoLint(StringRef Buffer, std::pair< size_t, size_t > LineStartAndEnd, NoLintType Type, StringRef DiagName)