[clang-tidy][NFC] Convert Lexer utils to use std::optional<Token> (#174809)

This bring a more unified api and avoid caveats like "return
``tok::unknown`` if not found.", which makes easier to forget error
checking.

---------

Co-authored-by: mitchell <zeyi2@nekoarch.cc>
This commit is contained in:
Baranov Victor 2026-01-31 22:21:18 +03:00 committed by GitHub
parent 1658456ccf
commit c951d76fd0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 70 additions and 60 deletions

View File

@ -127,16 +127,16 @@ static std::vector<std::pair<SourceLocation, StringRef>>
getCommentsBeforeLoc(ASTContext *Ctx, SourceLocation Loc) {
std::vector<std::pair<SourceLocation, StringRef>> Comments;
while (Loc.isValid()) {
const clang::Token Tok = utils::lexer::getPreviousToken(
const std::optional<Token> Tok = utils::lexer::getPreviousToken(
Loc, Ctx->getSourceManager(), Ctx->getLangOpts(),
/*SkipComments=*/false);
if (Tok.isNot(tok::comment))
if (!Tok || Tok->isNot(tok::comment))
break;
Loc = Tok.getLocation();
Loc = Tok->getLocation();
Comments.emplace_back(
Loc,
Lexer::getSourceText(CharSourceRange::getCharRange(
Loc, Loc.getLocWithOffset(Tok.getLength())),
Loc, Loc.getLocWithOffset(Tok->getLength())),
Ctx->getSourceManager(), Ctx->getLangOpts()));
}
return Comments;

View File

@ -141,10 +141,11 @@ void OptionalValueConversionCheck::check(
}
if (const auto *CallExpr =
Result.Nodes.getNodeAs<CXXMemberCallExpr>("member-call")) {
const SourceLocation Begin =
utils::lexer::getPreviousToken(CallExpr->getExprLoc(),
*Result.SourceManager, getLangOpts())
.getLocation();
const std::optional<Token> Tok = utils::lexer::getPreviousToken(
CallExpr->getExprLoc(), *Result.SourceManager, getLangOpts());
if (!Tok)
return;
const SourceLocation Begin = Tok->getLocation();
auto Diag =
diag(CallExpr->getExprLoc(),
"remove call to %0 to silence this warning", DiagnosticIDs::Note);

View File

@ -37,16 +37,16 @@ void SuspiciousSemicolonCheck::check(const MatchFinder::MatchResult &Result) {
return;
ASTContext &Ctxt = *Result.Context;
auto Token = utils::lexer::getPreviousToken(LocStart, Ctxt.getSourceManager(),
Ctxt.getLangOpts());
auto &SM = *Result.SourceManager;
const auto &SM = *Result.SourceManager;
const unsigned SemicolonLine = SM.getSpellingLineNumber(LocStart);
const auto *Statement = Result.Nodes.getNodeAs<Stmt>("stmt");
const bool IsIfStmt = isa<IfStmt>(Statement);
if (!IsIfStmt &&
SM.getSpellingLineNumber(Token.getLocation()) != SemicolonLine)
const std::optional<Token> PrevTok = utils::lexer::getPreviousToken(
LocStart, Ctxt.getSourceManager(), Ctxt.getLangOpts());
if (!PrevTok || (!IsIfStmt && SM.getSpellingLineNumber(
PrevTok->getLocation()) != SemicolonLine))
return;
const SourceLocation LocEnd = Semicolon->getEndLoc();
@ -55,6 +55,7 @@ void SuspiciousSemicolonCheck::check(const MatchFinder::MatchResult &Result) {
Lexer Lexer(SM.getLocForStartOfFile(FID), Ctxt.getLangOpts(),
Buffer.getBufferStart(), SM.getCharacterData(LocEnd) + 1,
Buffer.getBufferEnd());
Token Token;
if (Lexer.LexFromRawLexer(Token))
return;

View File

@ -149,18 +149,20 @@ struct InitializerInsertion {
"insertion represents a new initializer list.");
SourceLocation Location;
switch (Placement) {
case InitializerPlacement::New:
Location = utils::lexer::getPreviousToken(
Constructor.getBody()->getBeginLoc(),
Context.getSourceManager(), Context.getLangOpts())
.getLocation();
case InitializerPlacement::New: {
const std::optional<Token> Tok = utils::lexer::getPreviousToken(
Constructor.getBody()->getBeginLoc(), Context.getSourceManager(),
Context.getLangOpts());
Location = Tok ? Tok->getLocation() : SourceLocation{};
break;
case InitializerPlacement::Before:
Location = utils::lexer::getPreviousToken(
Where->getSourceRange().getBegin(),
Context.getSourceManager(), Context.getLangOpts())
.getLocation();
}
case InitializerPlacement::Before: {
const std::optional<Token> Tok = utils::lexer::getPreviousToken(
Where->getSourceRange().getBegin(), Context.getSourceManager(),
Context.getLangOpts());
Location = Tok ? Tok->getLocation() : SourceLocation{};
break;
}
case InitializerPlacement::After:
Location = Where->getRParenLoc();
break;

View File

@ -318,11 +318,11 @@ static std::optional<std::string> getConditionText(const Expr *ConditionExpr,
return std::nullopt;
const bool SkipComments = false;
Token PrevToken;
std::optional<Token> PrevToken;
std::tie(PrevToken, PrevTokenLoc) = utils::lexer::getPreviousTokenAndStart(
PrevTokenLoc, SM, LangOpts, SkipComments);
const bool EndsWithDoubleSlash =
PrevToken.is(tok::comment) &&
PrevToken && PrevToken->is(tok::comment) &&
Lexer::getSourceText(CharSourceRange::getCharRange(
PrevTokenLoc, PrevTokenLoc.getLocWithOffset(2)),
SM, LangOpts) == "//";

View File

@ -144,13 +144,15 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
SourceLocation EndLoc = MatchedDecl->getLocation();
while (true) {
const Token Prev = utils::lexer::getPreviousToken(StartLoc, SM, LO);
const std::optional<Token> Prev =
utils::lexer::getPreviousToken(StartLoc, SM, LO);
const std::optional<Token> Next =
utils::lexer::findNextTokenSkippingComments(EndLoc, SM, LO);
if (Prev.isNot(tok::l_paren) || !Next || Next->isNot(tok::r_paren))
if (!Prev || Prev->isNot(tok::l_paren) || !Next ||
Next->isNot(tok::r_paren))
break;
StartLoc = Prev.getLocation();
StartLoc = Prev->getLocation();
EndLoc = Next->getLocation();
}

View File

@ -52,16 +52,18 @@ void DeleteNullPointerCheck::check(const MatchFinder::MatchResult &Result) {
auto Diag = diag(
IfWithDelete->getBeginLoc(),
"'if' statement is unnecessary; deleting null pointer has no effect");
if (IfWithDelete->getElse())
if (IfWithDelete->hasElseStorage())
return;
// FIXME: generate fixit for this case.
const std::optional<Token> PrevTok = utils::lexer::getPreviousToken(
IfWithDelete->getThen()->getBeginLoc(), *Result.SourceManager,
Result.Context->getLangOpts());
if (!PrevTok)
return;
Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
IfWithDelete->getBeginLoc(),
utils::lexer::getPreviousToken(IfWithDelete->getThen()->getBeginLoc(),
*Result.SourceManager,
Result.Context->getLangOpts())
.getLocation()));
IfWithDelete->getBeginLoc(), PrevTok->getLocation()));
if (Compound) {
Diag << FixItHint::CreateRemoval(

View File

@ -157,8 +157,8 @@ declRanges(const DeclStmt *DS, const SourceManager &SM,
if (Start.isInvalid() || Start.isMacroID())
break;
const Token T = getPreviousToken(Start, SM, LangOpts);
if (T.is(tok::l_paren)) {
const std::optional<Token> T = getPreviousToken(Start, SM, LangOpts);
if (T && T->is(tok::l_paren)) {
Start = findPreviousTokenStart(Start, SM, LangOpts);
continue;
}

View File

@ -21,16 +21,16 @@ namespace clang::tidy::readability {
static SourceRange
getFullInitRangeInclWhitespaces(SourceRange Range, const SourceManager &SM,
const LangOptions &LangOpts) {
const Token PrevToken =
const std::optional<Token> PrevToken =
utils::lexer::getPreviousToken(Range.getBegin(), SM, LangOpts, false);
if (PrevToken.is(tok::unknown))
if (!PrevToken)
return Range;
if (PrevToken.isNot(tok::equal))
return {PrevToken.getEndLoc(), Range.getEnd()};
if (PrevToken->isNot(tok::equal))
return {PrevToken->getEndLoc(), Range.getEnd()};
return getFullInitRangeInclWhitespaces(
{PrevToken.getLocation(), Range.getEnd()}, SM, LangOpts);
{PrevToken->getLocation(), Range.getEnd()}, SM, LangOpts);
}
void RedundantMemberInitCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {

View File

@ -19,15 +19,15 @@ namespace clang::tidy::utils::fixit {
FixItHint changeVarDeclToReference(const VarDecl &Var, ASTContext &Context) {
SourceLocation AmpLocation = Var.getLocation();
auto Token = utils::lexer::getPreviousToken(
const std::optional<Token> Token = utils::lexer::getPreviousToken(
AmpLocation, Context.getSourceManager(), Context.getLangOpts());
// For parameter packs the '&' must go before the '...' token
if (Token.is(tok::ellipsis))
return FixItHint::CreateInsertion(Token.getLocation(), "&");
if (Token && Token->is(tok::ellipsis))
return FixItHint::CreateInsertion(Token->getLocation(), "&");
if (!Token.is(tok::unknown))
AmpLocation = Lexer::getLocForEndOfToken(Token.getLocation(), 0,
if (Token)
AmpLocation = Lexer::getLocForEndOfToken(Token->getLocation(), 0,
Context.getSourceManager(),
Context.getLangOpts());
return FixItHint::CreateInsertion(AmpLocation, "&");
@ -53,10 +53,9 @@ skipLParensBackwards(SourceLocation Start, const ASTContext &Context) {
return std::nullopt;
auto PreviousTokenLParen = [&Start, &Context]() {
Token T;
T = lexer::getPreviousToken(Start, Context.getSourceManager(),
Context.getLangOpts());
return T.is(tok::l_paren);
const std::optional<Token> T = lexer::getPreviousToken(
Start, Context.getSourceManager(), Context.getLangOpts());
return T && T->is(tok::l_paren);
};
while (Start.isValid() && PreviousTokenLParen())

View File

@ -13,7 +13,7 @@
namespace clang::tidy::utils::lexer {
std::pair<Token, SourceLocation>
std::pair<std::optional<Token>, SourceLocation>
getPreviousTokenAndStart(SourceLocation Location, const SourceManager &SM,
const LangOptions &LangOpts, bool SkipComments) {
const std::optional<Token> Tok =
@ -22,13 +22,13 @@ getPreviousTokenAndStart(SourceLocation Location, const SourceManager &SM,
if (Tok.has_value())
return {*Tok, Lexer::GetBeginningOfToken(Tok->getLocation(), SM, LangOpts)};
Token Token;
Token.setKind(tok::unknown);
return {Token, SourceLocation()};
return {std::nullopt, SourceLocation()};
}
Token getPreviousToken(SourceLocation Location, const SourceManager &SM,
const LangOptions &LangOpts, bool SkipComments) {
std::optional<Token> getPreviousToken(SourceLocation Location,
const SourceManager &SM,
const LangOptions &LangOpts,
bool SkipComments) {
auto [Token, Start] =
getPreviousTokenAndStart(Location, SM, LangOpts, SkipComments);
return Token;

View File

@ -21,10 +21,13 @@ class Stmt;
namespace tidy::utils::lexer {
/// Returns previous token or ``tok::unknown`` if not found.
Token getPreviousToken(SourceLocation Location, const SourceManager &SM,
const LangOptions &LangOpts, bool SkipComments = true);
std::pair<Token, SourceLocation>
/// Returns previous token or ``std::nullopt`` if not found.
std::optional<Token> getPreviousToken(SourceLocation Location,
const SourceManager &SM,
const LangOptions &LangOpts,
bool SkipComments = true);
std::pair<std::optional<Token>, SourceLocation>
getPreviousTokenAndStart(SourceLocation Location, const SourceManager &SM,
const LangOptions &LangOpts, bool SkipComments = true);