Skip to content

Commit ab898f3

Browse files
authored
[clang-tidy][NFC] Do less unnecessary work in NoLintDirectiveHandler (#147553)
Summary: - `NoLintBlockToken` is too big: it stores a whole `NoLintToken` inside itself, when all it needs from that `NoLintToken` is its `Pos` and `ChecksGlob`. - `formNoLintBlocks` builds up a vector of unmatched tokens, which are later transformed into errors. We can skip the middle step and make `formNoLintBlocks` create errors directly. - In `generateCache`, the line `Cache[FileName] = ...;` default-constructs a cache entry only to immediately overwrite it. We can avoid that by using `Cache.try_emplace(FileName, ...);` instead. - `NoLintToken`'s constructor takes `const std::optional<std::string>&` when all it needs is `const std::optional<StringRef>&`. This forces its caller, `getNoLints`, to create temporary strings. - `NoLintToken::checks` returns `std::optional<std::string>` by value, creating unnecessary copies in `formNoLintBlocks`.
1 parent ee312fc commit ab898f3

File tree

2 files changed

+54
-61
lines changed

2 files changed

+54
-61
lines changed

clang-tools-extra/clang-tidy/NoLintDirectiveHandler.cpp

Lines changed: 54 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,9 @@
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

3433
namespace 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

102104
private:
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.
156157
class NoLintBlockToken {
157158
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)) {}
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

173170
private:
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`.
183200
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) {
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>.
@@ -356,39 +376,14 @@ bool NoLintDirectiveHandler::Impl::diagHasNoLint(
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.
379380
void 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
//===----------------------------------------------------------------------===//

clang-tools-extra/clang-tidy/NoLintDirectiveHandler.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@ class NoLintDirectiveHandler {
3131
public:
3232
NoLintDirectiveHandler();
3333
~NoLintDirectiveHandler();
34-
NoLintDirectiveHandler(const NoLintDirectiveHandler &) = delete;
35-
NoLintDirectiveHandler &operator=(const NoLintDirectiveHandler &) = delete;
3634

3735
bool shouldSuppress(DiagnosticsEngine::Level DiagLevel,
3836
const Diagnostic &Diag, llvm::StringRef DiagName,

0 commit comments

Comments
 (0)