blob: f769db8840f796f4817008dcf56acefe49840a6f [file] [log] [blame]
//===--- LexerUtils.cpp - clang-tidy---------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "LexerUtils.h"
#include "clang/AST/AST.h"
#include "clang/Basic/SourceManager.h"
namespace clang {
namespace tidy {
namespace utils {
namespace lexer {
Token getPreviousToken(SourceLocation Location, const SourceManager &SM,
const LangOptions &LangOpts, bool SkipComments) {
Token Token;
Token.setKind(tok::unknown);
Location = Location.getLocWithOffset(-1);
if (Location.isInvalid())
return Token;
auto StartOfFile = SM.getLocForStartOfFile(SM.getFileID(Location));
while (Location != StartOfFile) {
Location = Lexer::GetBeginningOfToken(Location, SM, LangOpts);
if (!Lexer::getRawToken(Location, Token, SM, LangOpts) &&
(!SkipComments || !Token.is(tok::comment))) {
break;
}
Location = Location.getLocWithOffset(-1);
}
return Token;
}
SourceLocation findPreviousTokenStart(SourceLocation Start,
const SourceManager &SM,
const LangOptions &LangOpts) {
if (Start.isInvalid() || Start.isMacroID())
return SourceLocation();
SourceLocation BeforeStart = Start.getLocWithOffset(-1);
if (BeforeStart.isInvalid() || BeforeStart.isMacroID())
return SourceLocation();
return Lexer::GetBeginningOfToken(BeforeStart, SM, LangOpts);
}
SourceLocation findPreviousTokenKind(SourceLocation Start,
const SourceManager &SM,
const LangOptions &LangOpts,
tok::TokenKind TK) {
if (Start.isInvalid() || Start.isMacroID())
return SourceLocation();
while (true) {
SourceLocation L = findPreviousTokenStart(Start, SM, LangOpts);
if (L.isInvalid() || L.isMacroID())
return SourceLocation();
Token T;
if (Lexer::getRawToken(L, T, SM, LangOpts, /*IgnoreWhiteSpace=*/true))
return SourceLocation();
if (T.is(TK))
return T.getLocation();
Start = L;
}
}
SourceLocation findNextTerminator(SourceLocation Start, const SourceManager &SM,
const LangOptions &LangOpts) {
return findNextAnyTokenKind(Start, SM, LangOpts, tok::comma, tok::semi);
}
Optional<Token> findNextTokenSkippingComments(SourceLocation Start,
const SourceManager &SM,
const LangOptions &LangOpts) {
Optional<Token> CurrentToken;
do {
CurrentToken = Lexer::findNextToken(Start, SM, LangOpts);
} while (CurrentToken && CurrentToken->is(tok::comment));
return CurrentToken;
}
bool rangeContainsExpansionsOrDirectives(SourceRange Range,
const SourceManager &SM,
const LangOptions &LangOpts) {
assert(Range.isValid() && "Invalid Range for relexing provided");
SourceLocation Loc = Range.getBegin();
while (Loc < Range.getEnd()) {
if (Loc.isMacroID())
return true;
llvm::Optional<Token> Tok = Lexer::findNextToken(Loc, SM, LangOpts);
if (!Tok)
return true;
if (Tok->is(tok::hash))
return true;
Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts).getLocWithOffset(1);
}
return false;
}
llvm::Optional<Token> getQualifyingToken(tok::TokenKind TK,
CharSourceRange Range,
const ASTContext &Context,
const SourceManager &SM) {
assert((TK == tok::kw_const || TK == tok::kw_volatile ||
TK == tok::kw_restrict) &&
"TK is not a qualifier keyword");
std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Range.getBegin());
StringRef File = SM.getBufferData(LocInfo.first);
Lexer RawLexer(SM.getLocForStartOfFile(LocInfo.first), Context.getLangOpts(),
File.begin(), File.data() + LocInfo.second, File.end());
llvm::Optional<Token> LastMatchBeforeTemplate;
llvm::Optional<Token> LastMatchAfterTemplate;
bool SawTemplate = false;
Token Tok;
while (!RawLexer.LexFromRawLexer(Tok) &&
Range.getEnd() != Tok.getLocation() &&
!SM.isBeforeInTranslationUnit(Range.getEnd(), Tok.getLocation())) {
if (Tok.is(tok::raw_identifier)) {
IdentifierInfo &Info = Context.Idents.get(
StringRef(SM.getCharacterData(Tok.getLocation()), Tok.getLength()));
Tok.setIdentifierInfo(&Info);
Tok.setKind(Info.getTokenID());
}
if (Tok.is(tok::less))
SawTemplate = true;
else if (Tok.isOneOf(tok::greater, tok::greatergreater))
LastMatchAfterTemplate = None;
else if (Tok.is(TK)) {
if (SawTemplate)
LastMatchAfterTemplate = Tok;
else
LastMatchBeforeTemplate = Tok;
}
}
return LastMatchAfterTemplate != None ? LastMatchAfterTemplate
: LastMatchBeforeTemplate;
}
static bool breakAndReturnEnd(const Stmt &S) {
return isa<CompoundStmt, DeclStmt, NullStmt>(S);
}
static bool breakAndReturnEndPlus1Token(const Stmt &S) {
return isa<Expr, DoStmt, ReturnStmt, BreakStmt, ContinueStmt, GotoStmt, SEHLeaveStmt>(S);
}
// Given a Stmt which does not include it's semicolon this method returns the
// SourceLocation of the semicolon.
static SourceLocation getSemicolonAfterStmtEndLoc(const SourceLocation &EndLoc,
const SourceManager &SM,
const LangOptions &LangOpts) {
if (EndLoc.isMacroID()) {
// Assuming EndLoc points to a function call foo within macro F.
// This method is supposed to return location of the semicolon within
// those macro arguments:
// F ( foo() ; )
// ^ EndLoc ^ SpellingLoc ^ next token of SpellingLoc
const SourceLocation SpellingLoc = SM.getSpellingLoc(EndLoc);
Optional<Token> NextTok =
findNextTokenSkippingComments(SpellingLoc, SM, LangOpts);
// Was the next token found successfully?
// All macro issues are simply resolved by ensuring it's a semicolon.
if (NextTok && NextTok->is(tok::TokenKind::semi)) {
// Ideally this would return `F` with spelling location `;` (NextTok)
// following the example above. For now simply return NextTok location.
return NextTok->getLocation();
}
// Fallthrough to 'normal handling'.
// F ( foo() ) ;
// ^ EndLoc ^ SpellingLoc ) ^ next token of EndLoc
}
Optional<Token> NextTok = findNextTokenSkippingComments(EndLoc, SM, LangOpts);
// Testing for semicolon again avoids some issues with macros.
if (NextTok && NextTok->is(tok::TokenKind::semi))
return NextTok->getLocation();
return SourceLocation();
}
SourceLocation getUnifiedEndLoc(const Stmt &S, const SourceManager &SM,
const LangOptions &LangOpts) {
const Stmt *LastChild = &S;
while (!LastChild->children().empty() && !breakAndReturnEnd(*LastChild) &&
!breakAndReturnEndPlus1Token(*LastChild)) {
for (const Stmt *Child : LastChild->children())
LastChild = Child;
}
if (!breakAndReturnEnd(*LastChild) &&
breakAndReturnEndPlus1Token(*LastChild))
return getSemicolonAfterStmtEndLoc(S.getEndLoc(), SM, LangOpts);
return S.getEndLoc();
}
} // namespace lexer
} // namespace utils
} // namespace tidy
} // namespace clang