258 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "LexerUtils.h"10#include "clang/AST/AST.h"11#include "clang/Basic/SourceManager.h"12#include <optional>13#include <utility>14 15namespace clang::tidy::utils::lexer {16 17std::pair<Token, SourceLocation>18getPreviousTokenAndStart(SourceLocation Location, const SourceManager &SM,19 const LangOptions &LangOpts, bool SkipComments) {20 const std::optional<Token> Tok =21 Lexer::findPreviousToken(Location, SM, LangOpts, !SkipComments);22 23 if (Tok.has_value()) {24 return {*Tok, Lexer::GetBeginningOfToken(Tok->getLocation(), SM, LangOpts)};25 }26 27 Token Token;28 Token.setKind(tok::unknown);29 return {Token, SourceLocation()};30}31 32Token getPreviousToken(SourceLocation Location, const SourceManager &SM,33 const LangOptions &LangOpts, bool SkipComments) {34 auto [Token, Start] =35 getPreviousTokenAndStart(Location, SM, LangOpts, SkipComments);36 return Token;37}38 39SourceLocation findPreviousTokenStart(SourceLocation Start,40 const SourceManager &SM,41 const LangOptions &LangOpts) {42 if (Start.isInvalid() || Start.isMacroID())43 return {};44 45 const SourceLocation BeforeStart = Start.getLocWithOffset(-1);46 if (BeforeStart.isInvalid() || BeforeStart.isMacroID())47 return {};48 49 return Lexer::GetBeginningOfToken(BeforeStart, SM, LangOpts);50}51 52SourceLocation findPreviousTokenKind(SourceLocation Start,53 const SourceManager &SM,54 const LangOptions &LangOpts,55 tok::TokenKind TK) {56 if (Start.isInvalid() || Start.isMacroID())57 return {};58 59 while (true) {60 const SourceLocation L = findPreviousTokenStart(Start, SM, LangOpts);61 if (L.isInvalid() || L.isMacroID())62 return {};63 64 Token T;65 if (Lexer::getRawToken(L, T, SM, LangOpts, /*IgnoreWhiteSpace=*/true))66 return {};67 68 if (T.is(TK))69 return T.getLocation();70 71 Start = L;72 }73}74 75SourceLocation findNextTerminator(SourceLocation Start, const SourceManager &SM,76 const LangOptions &LangOpts) {77 return findNextAnyTokenKind(Start, SM, LangOpts, tok::comma, tok::semi);78}79 80std::optional<Token>81findNextTokenSkippingComments(SourceLocation Start, const SourceManager &SM,82 const LangOptions &LangOpts) {83 while (Start.isValid()) {84 std::optional<Token> CurrentToken =85 Lexer::findNextToken(Start, SM, LangOpts);86 if (!CurrentToken || !CurrentToken->is(tok::comment))87 return CurrentToken;88 89 Start = CurrentToken->getLocation();90 }91 92 return std::nullopt;93}94 95bool rangeContainsExpansionsOrDirectives(SourceRange Range,96 const SourceManager &SM,97 const LangOptions &LangOpts) {98 assert(Range.isValid() && "Invalid Range for relexing provided");99 SourceLocation Loc = Range.getBegin();100 101 while (Loc <= Range.getEnd()) {102 if (Loc.isMacroID())103 return true;104 105 std::optional<Token> Tok = Lexer::findNextToken(Loc, SM, LangOpts);106 107 if (!Tok)108 return true;109 110 if (Tok->is(tok::hash))111 return true;112 113 Loc = Tok->getLocation();114 }115 116 return false;117}118 119std::optional<Token> getQualifyingToken(tok::TokenKind TK,120 CharSourceRange Range,121 const ASTContext &Context,122 const SourceManager &SM) {123 assert((TK == tok::kw_const || TK == tok::kw_volatile ||124 TK == tok::kw_restrict) &&125 "TK is not a qualifier keyword");126 const std::pair<FileID, unsigned> LocInfo =127 SM.getDecomposedLoc(Range.getBegin());128 const StringRef File = SM.getBufferData(LocInfo.first);129 Lexer RawLexer(SM.getLocForStartOfFile(LocInfo.first), Context.getLangOpts(),130 File.begin(), File.data() + LocInfo.second, File.end());131 std::optional<Token> LastMatchBeforeTemplate;132 std::optional<Token> LastMatchAfterTemplate;133 bool SawTemplate = false;134 Token Tok;135 while (!RawLexer.LexFromRawLexer(Tok) &&136 Range.getEnd() != Tok.getLocation() &&137 !SM.isBeforeInTranslationUnit(Range.getEnd(), Tok.getLocation())) {138 if (Tok.is(tok::raw_identifier)) {139 IdentifierInfo &Info = Context.Idents.get(140 StringRef(SM.getCharacterData(Tok.getLocation()), Tok.getLength()));141 Tok.setIdentifierInfo(&Info);142 Tok.setKind(Info.getTokenID());143 }144 if (Tok.is(tok::less))145 SawTemplate = true;146 else if (Tok.isOneOf(tok::greater, tok::greatergreater))147 LastMatchAfterTemplate = std::nullopt;148 else if (Tok.is(TK)) {149 if (SawTemplate)150 LastMatchAfterTemplate = Tok;151 else152 LastMatchBeforeTemplate = Tok;153 }154 }155 return LastMatchAfterTemplate != std::nullopt ? LastMatchAfterTemplate156 : LastMatchBeforeTemplate;157}158 159static bool breakAndReturnEnd(const Stmt &S) {160 return isa<CompoundStmt, DeclStmt, NullStmt>(S);161}162 163static bool breakAndReturnEndPlus1Token(const Stmt &S) {164 return isa<Expr, DoStmt, ReturnStmt, BreakStmt, ContinueStmt, GotoStmt,165 SEHLeaveStmt>(S);166}167 168// Given a Stmt which does not include it's semicolon this method returns the169// SourceLocation of the semicolon.170static SourceLocation getSemicolonAfterStmtEndLoc(const SourceLocation &EndLoc,171 const SourceManager &SM,172 const LangOptions &LangOpts) {173 if (EndLoc.isMacroID()) {174 // Assuming EndLoc points to a function call foo within macro F.175 // This method is supposed to return location of the semicolon within176 // those macro arguments:177 // F ( foo() ; )178 // ^ EndLoc ^ SpellingLoc ^ next token of SpellingLoc179 const SourceLocation SpellingLoc = SM.getSpellingLoc(EndLoc);180 std::optional<Token> NextTok =181 findNextTokenSkippingComments(SpellingLoc, SM, LangOpts);182 183 // Was the next token found successfully?184 // All macro issues are simply resolved by ensuring it's a semicolon.185 if (NextTok && NextTok->is(tok::TokenKind::semi)) {186 // Ideally this would return `F` with spelling location `;` (NextTok)187 // following the example above. For now simply return NextTok location.188 return NextTok->getLocation();189 }190 191 // Fallthrough to 'normal handling'.192 // F ( foo() ) ;193 // ^ EndLoc ^ SpellingLoc ) ^ next token of EndLoc194 }195 196 std::optional<Token> NextTok =197 findNextTokenSkippingComments(EndLoc, SM, LangOpts);198 199 // Testing for semicolon again avoids some issues with macros.200 if (NextTok && NextTok->is(tok::TokenKind::semi))201 return NextTok->getLocation();202 203 return {};204}205 206SourceLocation getUnifiedEndLoc(const Stmt &S, const SourceManager &SM,207 const LangOptions &LangOpts) {208 const Stmt *LastChild = &S;209 while (!LastChild->children().empty() && !breakAndReturnEnd(*LastChild) &&210 !breakAndReturnEndPlus1Token(*LastChild)) {211 for (const Stmt *Child : LastChild->children())212 LastChild = Child;213 }214 215 if (!breakAndReturnEnd(*LastChild) && breakAndReturnEndPlus1Token(*LastChild))216 return getSemicolonAfterStmtEndLoc(S.getEndLoc(), SM, LangOpts);217 218 return S.getEndLoc();219}220 221SourceLocation getLocationForNoexceptSpecifier(const FunctionDecl *FuncDecl,222 const SourceManager &SM) {223 if (!FuncDecl)224 return {};225 226 const LangOptions &LangOpts = FuncDecl->getLangOpts();227 228 if (FuncDecl->getNumParams() == 0) {229 // Start at the beginning of the function declaration, and find the closing230 // parenthesis after which we would place the noexcept specifier.231 Token CurrentToken;232 SourceLocation CurrentLocation = FuncDecl->getBeginLoc();233 while (!Lexer::getRawToken(CurrentLocation, CurrentToken, SM, LangOpts,234 true)) {235 if (CurrentToken.is(tok::r_paren))236 return CurrentLocation.getLocWithOffset(1);237 238 CurrentLocation = CurrentToken.getEndLoc();239 }240 241 // Failed to find the closing parenthesis, so just return an invalid242 // SourceLocation.243 return {};244 }245 246 // FunctionDecl with parameters247 const SourceLocation NoexceptLoc =248 FuncDecl->getParamDecl(FuncDecl->getNumParams() - 1)->getEndLoc();249 if (NoexceptLoc.isValid())250 return Lexer::findLocationAfterToken(251 NoexceptLoc, tok::r_paren, SM, LangOpts,252 /*SkipTrailingWhitespaceAndNewLine=*/true);253 254 return {};255}256 257} // namespace clang::tidy::utils::lexer258