blob: 0a0c4cdc9d3b584ce74b21bcf96560a825aace3e [file] [log] [blame]
//===--- SourceLocationUtilities.cpp - Source location helper functions ---===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "SourceLocationUtilities.h"
#include "clang/AST/Stmt.h"
#include "clang/Lex/Lexer.h"
#include <limits>
namespace clang {
namespace tooling {
SourceLocation findLastLocationOfSourceConstruct(SourceLocation HeaderEnd,
const Stmt *Body,
const SourceManager &SM) {
SourceLocation BodyStart = SM.getSpellingLoc(Body->getLocStart());
unsigned BodyLine = SM.getSpellingLineNumber(BodyStart);
unsigned HeaderLine = SM.getSpellingLineNumber(HeaderEnd);
if (BodyLine > HeaderLine) {
// The Last location on the previous line if the body is not on the same
// line as the last known location.
SourceLocation LineLocThatPrecedesBody =
SM.translateLineCol(SM.getFileID(BodyStart), BodyLine - 1,
std::numeric_limits<unsigned>::max());
if (LineLocThatPrecedesBody.isValid())
return LineLocThatPrecedesBody;
}
// We want to include the location of the '{'.
return isa<CompoundStmt>(Body) ? BodyStart : BodyStart.getLocWithOffset(-1);
}
SourceLocation findFirstLocationOfSourceConstruct(SourceLocation HeaderStart,
const Stmt *PreviousBody,
const SourceManager &SM) {
if (!isa<CompoundStmt>(PreviousBody))
return HeaderStart;
SourceLocation BodyEnd = SM.getSpellingLoc(PreviousBody->getLocEnd());
unsigned BodyLine = SM.getSpellingLineNumber(BodyEnd);
unsigned HeaderLine = SM.getSpellingLineNumber(HeaderStart);
if (BodyLine >= HeaderLine)
return BodyEnd;
return HeaderStart;
}
bool isLocationInAnyRange(SourceLocation Location, ArrayRef<SourceRange> Ranges,
const SourceManager &SM) {
for (const SourceRange &Range : Ranges) {
if (!isPointWithin(Location, Range.getBegin(), Range.getEnd(), SM))
continue;
return true;
}
return false;
}
SourceLocation getPreciseTokenLocEnd(SourceLocation Loc,
const SourceManager &SM,
const LangOptions &LangOpts) {
return Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts);
}
SourceLocation findClosingParenLocEnd(SourceLocation LastKnownLoc,
const SourceManager &SM,
const LangOptions &LangOpts) {
return Lexer::findLocationAfterToken(
LastKnownLoc, tok::r_paren, SM, LangOpts,
/*SkipTrailingWhitespaceAndNewLine=*/false);
}
SourceRange getRangeOfNextToken(SourceLocation Loc, tok::TokenKind Kind,
const SourceManager &SM,
const LangOptions &LangOpts) {
SourceLocation NextLoc =
Lexer::findLocationAfterToken(Loc, Kind, SM, LangOpts,
/*SkipTrailingWhitespaceAndNewLine=*/false);
if (NextLoc.isInvalid())
return SourceRange();
return SourceRange(
Lexer::GetBeginningOfToken(NextLoc.getLocWithOffset(-1), SM, LangOpts),
NextLoc);
}
SourceLocation findLastNonCompoundLocation(const Stmt *S) {
const auto *CS = dyn_cast<CompoundStmt>(S);
if (!CS)
return S->getLocEnd();
return CS->body_back() ? CS->body_back()->getLocEnd() : SourceLocation();
}
bool areOnSameLine(SourceLocation Loc1, SourceLocation Loc2,
const SourceManager &SM) {
return !Loc1.isMacroID() && !Loc2.isMacroID() &&
SM.getSpellingLineNumber(Loc1) == SM.getSpellingLineNumber(Loc2);
}
SourceLocation
getLastLineLocationUnlessItHasOtherTokens(SourceLocation SpellingLoc,
const SourceManager &SM,
const LangOptions &LangOpts) {
assert(!SpellingLoc.isMacroID() && "Expecting a spelling location");
SourceLocation NextTokenLoc =
Lexer::findNextTokenLocationAfterTokenAt(SpellingLoc, SM, LangOpts);
if (NextTokenLoc.isValid()) {
bool IsSameLine = areOnSameLine(SpellingLoc, NextTokenLoc, SM);
if (IsSameLine) {
// Could be a ';' on the same line, so try looking after the ';'
if (isSemicolonAtLocation(NextTokenLoc, SM, LangOpts))
return getLastLineLocationUnlessItHasOtherTokens(NextTokenLoc, SM,
LangOpts);
} else {
SourceLocation LastLoc = SM.translateLineCol(
SM.getFileID(SpellingLoc), SM.getSpellingLineNumber(SpellingLoc),
std::numeric_limits<unsigned>::max());
if (LastLoc.isValid())
return LastLoc;
}
}
return getPreciseTokenLocEnd(SpellingLoc, SM, LangOpts);
}
bool isSemicolonAtLocation(SourceLocation TokenLoc, const SourceManager &SM,
const LangOptions &LangOpts) {
return Lexer::getSourceText(
CharSourceRange::getTokenRange(TokenLoc, TokenLoc), SM,
LangOpts) == ";";
}
SourceRange trimSelectionRange(SourceRange Range, const SourceManager &SM,
const LangOptions &LangOpts) {
bool IsInvalid = false;
StringRef Text = Lexer::getSourceText(CharSourceRange::getCharRange(Range),
SM, LangOpts, &IsInvalid);
if (IsInvalid || Text.empty())
return Range;
assert(Range.getBegin().isFileID() && "Not a file range!");
std::string Source = Text.str();
Lexer Lex(Range.getBegin(), LangOpts, Source.c_str(), Source.c_str(),
Source.c_str() + Source.size());
// Get comment tokens as well.
Lex.SetCommentRetentionState(true);
SourceLocation StartLoc, EndLoc;
while (true) {
Token Tok;
Lex.LexFromRawLexer(Tok);
if (Tok.getKind() == tok::eof)
break;
if (StartLoc.isInvalid())
StartLoc = Tok.getLocation();
if (Tok.getKind() != tok::semi)
EndLoc = Tok.getEndLoc();
}
return StartLoc.isValid() && EndLoc.isValid() ? SourceRange(StartLoc, EndLoc)
: SourceRange();
}
/// Tokenize the given file and check if it contains a comment that ends at the
/// given location.
static SourceLocation findCommentThatEndsAt(FileID FID,
SourceLocation StartOfFile,
const SourceManager &SM,
const LangOptions &LangOpts,
SourceLocation ExpectedEndLoc) {
// Try to load the file buffer.
bool InvalidTemp = false;
StringRef File = SM.getBufferData(FID, &InvalidTemp);
if (InvalidTemp)
return SourceLocation();
// Search for the comment that ends at the given location.
Lexer Lex(StartOfFile, LangOpts, File.begin(), File.begin(), File.end());
Lex.SetCommentRetentionState(true);
Token Tok;
while (!Lex.LexFromRawLexer(Tok)) {
if (Tok.is(tok::comment) && Tok.getEndLoc() == ExpectedEndLoc)
return Tok.getLocation();
}
// Find the token.
return SourceLocation();
}
SourceLocation getLocationOfPrecedingComment(SourceLocation Location,
const SourceManager &SM,
const LangOptions &LangOpts) {
SourceLocation PrevResult = Location;
SourceLocation Result = Location;
if (Result.isMacroID())
Result = SM.getExpansionLoc(Result);
FileID FID = SM.getFileID(Result);
SourceLocation StartOfFile = SM.getLocForStartOfFile(FID);
Token Tok;
Tok.setKind(tok::unknown);
SourceLocation TokenLoc = Result;
auto GetPreviousToken = [&]() -> bool {
TokenLoc =
Lexer::GetBeginningOfToken(TokenLoc.getLocWithOffset(-1), SM, LangOpts);
return !Lexer::getRawToken(TokenLoc, Tok, SM, LangOpts);
};
// Look for a comment token.
while (TokenLoc != StartOfFile) {
bool LocHasToken = GetPreviousToken();
if (LocHasToken && Tok.is(tok::slash)) {
// Check if this the end of a multiline '/*' comment before returning.
SourceLocation CommentLoc = findCommentThatEndsAt(
FID, StartOfFile, SM, LangOpts, Tok.getEndLoc());
return CommentLoc.isInvalid() ? Result : CommentLoc;
}
if (LocHasToken && Tok.isNot(tok::comment))
break;
if (!LocHasToken)
continue;
// We found a preceding comment. Check if there are other preceding
// comments.
PrevResult = Result;
Result = Tok.getLocation();
while (TokenLoc != StartOfFile) {
bool LocHasToken = GetPreviousToken();
if (LocHasToken && Tok.isNot(tok::comment)) {
// Reset the result to the previous location if this comment trails
// another token on the same line.
if (SM.getSpellingLineNumber(Tok.getEndLoc()) ==
SM.getSpellingLineNumber(Result))
Result = PrevResult;
break;
}
if (!LocHasToken)
continue;
// The location of this comment is accepted only when the next comment
// is located immediately after this comment.
if (SM.getSpellingLineNumber(Tok.getEndLoc()) !=
SM.getSpellingLineNumber(Result) - 1)
break;
PrevResult = Result;
Result = Tok.getLocation();
}
break;
}
return Result;
}
SourceLocation getLocationOfPrecedingToken(SourceLocation Loc,
const SourceManager &SM,
const LangOptions &LangOpts) {
SourceLocation Result = Loc;
if (Result.isMacroID())
Result = SM.getExpansionLoc(Result);
FileID FID = SM.getFileID(Result);
SourceLocation StartOfFile = SM.getLocForStartOfFile(FID);
if (Loc == StartOfFile)
return SourceLocation();
return Lexer::GetBeginningOfToken(Result.getLocWithOffset(-1), SM, LangOpts);
}
} // end namespace tooling
} // end namespace clang