blob: 685750eec35e156434a0970b839de5441fc05cb4 [file] [log] [blame]
//===--- UseOverride.cpp - clang-tidy -------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "UseOverride.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
void UseOverride::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(methodDecl(isOverride()).bind("method"), this);
}
// Re-lex the tokens to get precise locations to insert 'override' and remove
// 'virtual'.
static SmallVector<Token, 16> ParseTokens(CharSourceRange Range,
const SourceManager &Sources,
LangOptions LangOpts) {
std::pair<FileID, unsigned> LocInfo =
Sources.getDecomposedLoc(Range.getBegin());
StringRef File = Sources.getBufferData(LocInfo.first);
const char *TokenBegin = File.data() + LocInfo.second;
Lexer RawLexer(Sources.getLocForStartOfFile(LocInfo.first), LangOpts,
File.begin(), TokenBegin, File.end());
SmallVector<Token, 16> Tokens;
Token Tok;
while (!RawLexer.LexFromRawLexer(Tok)) {
if (Tok.is(tok::semi) || Tok.is(tok::l_brace))
break;
if (Sources.isBeforeInTranslationUnit(Range.getEnd(), Tok.getLocation()))
break;
Tokens.push_back(Tok);
}
return Tokens;
}
static StringRef GetText(const Token &Tok, const SourceManager &Sources) {
return StringRef(Sources.getCharacterData(Tok.getLocation()),
Tok.getLength());
}
void UseOverride::check(const MatchFinder::MatchResult &Result) {
const FunctionDecl *Method = Result.Nodes.getStmtAs<FunctionDecl>("method");
const SourceManager &Sources = *Result.SourceManager;
assert(Method != nullptr);
if (Method->getInstantiatedFromMemberFunction() != nullptr)
Method = Method->getInstantiatedFromMemberFunction();
if (Method->isImplicit() || Method->getLocation().isMacroID() ||
Method->isOutOfLine())
return;
bool HasVirtual = Method->isVirtualAsWritten();
bool HasOverride = Method->getAttr<OverrideAttr>();
bool HasFinal = Method->getAttr<FinalAttr>();
bool OnlyVirtualSpecified = HasVirtual && !HasOverride && !HasFinal;
unsigned KeywordCount = HasVirtual + HasOverride + HasFinal;
if (!OnlyVirtualSpecified && KeywordCount == 1)
return; // Nothing to do.
DiagnosticBuilder Diag = diag(
Method->getLocation(),
OnlyVirtualSpecified
? "Prefer using 'override' or 'final' instead of 'virtual'"
: "Use exactly one of 'virtual', 'override' or (rarely) 'final'");
CharSourceRange FileRange = Lexer::makeFileCharRange(
CharSourceRange::getTokenRange(Method->getSourceRange()), Sources,
Result.Context->getLangOpts());
if (!FileRange.isValid())
return;
// FIXME: Instead of re-lexing and looking for specific macros such as
// 'ABSTRACT', properly store the location of 'virtual' and '= 0' in each
// FunctionDecl.
SmallVector<Token, 16> Tokens = ParseTokens(FileRange, Sources,
Result.Context->getLangOpts());
// Add 'override' on inline declarations that don't already have it.
if (!HasFinal && !HasOverride) {
SourceLocation InsertLoc;
StringRef ReplacementText = "override ";
if (Method->hasAttrs()) {
for (const clang::Attr *A : Method->getAttrs()) {
if (!A->isImplicit()) {
InsertLoc = Sources.getExpansionLoc(A->getLocation());
break;
}
}
}
if (InsertLoc.isInvalid() && Method->doesThisDeclarationHaveABody() &&
Method->getBody() && !Method->isDefaulted())
InsertLoc = Method->getBody()->getLocStart();
if (!InsertLoc.isValid()) {
if (Tokens.size() > 2 && GetText(Tokens.back(), Sources) == "0" &&
GetText(Tokens[Tokens.size() - 2], Sources) == "=") {
InsertLoc = Tokens[Tokens.size() - 2].getLocation();
} else if (GetText(Tokens.back(), Sources) == "ABSTRACT") {
InsertLoc = Tokens.back().getLocation();
}
}
if (!InsertLoc.isValid()) {
InsertLoc = FileRange.getEnd();
ReplacementText = " override";
}
Diag << FixItHint::CreateInsertion(InsertLoc, ReplacementText);
}
if (HasFinal && HasOverride) {
SourceLocation OverrideLoc = Method->getAttr<OverrideAttr>()->getLocation();
Diag << FixItHint::CreateRemoval(
CharSourceRange::getTokenRange(OverrideLoc, OverrideLoc));
}
if (Method->isVirtualAsWritten()) {
for (Token Tok : Tokens) {
if (Tok.is(tok::raw_identifier) && GetText(Tok, Sources) == "virtual") {
Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
Tok.getLocation(), Tok.getLocation()));
break;
}
}
}
}
} // namespace tidy
} // namespace clang