11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.h"
15 using namespace clang::ast_matchers;
21 void UseOverrideCheck::registerMatchers(MatchFinder *
Finder) {
23 if (getLangOpts().CPlusPlus11)
24 Finder->addMatcher(cxxMethodDecl(isOverride()).bind(
"method"),
this);
29 static SmallVector<Token, 16>
31 const SourceManager &Sources = *Result.SourceManager;
32 std::pair<FileID, unsigned> LocInfo =
33 Sources.getDecomposedLoc(Range.getBegin());
34 StringRef
File = Sources.getBufferData(LocInfo.first);
35 const char *TokenBegin = File.data() + LocInfo.second;
36 Lexer RawLexer(Sources.getLocForStartOfFile(LocInfo.first),
37 Result.Context->getLangOpts(), File.begin(), TokenBegin,
39 SmallVector<Token, 16>
Tokens;
41 while (!RawLexer.LexFromRawLexer(Tok)) {
42 if (Tok.is(tok::semi) || Tok.is(tok::l_brace))
44 if (Sources.isBeforeInTranslationUnit(Range.getEnd(), Tok.getLocation()))
46 if (Tok.is(tok::raw_identifier)) {
47 IdentifierInfo &Info = Result.Context->Idents.get(StringRef(
48 Sources.getCharacterData(Tok.getLocation()), Tok.getLength()));
49 Tok.setIdentifierInfo(&Info);
50 Tok.setKind(Info.getTokenID());
52 Tokens.push_back(Tok);
57 static StringRef
GetText(
const Token &Tok,
const SourceManager &Sources) {
58 return StringRef(Sources.getCharacterData(Tok.getLocation()),
62 void UseOverrideCheck::check(
const MatchFinder::MatchResult &
Result) {
63 const auto *Method = Result.Nodes.getNodeAs<FunctionDecl>(
"method");
64 const SourceManager &Sources = *Result.SourceManager;
66 assert(Method !=
nullptr);
67 if (Method->getInstantiatedFromMemberFunction() !=
nullptr)
68 Method = Method->getInstantiatedFromMemberFunction();
70 if (Method->isImplicit() || Method->getLocation().isMacroID() ||
71 Method->isOutOfLine())
74 bool HasVirtual = Method->isVirtualAsWritten();
75 bool HasOverride = Method->getAttr<OverrideAttr>();
76 bool HasFinal = Method->getAttr<FinalAttr>();
78 bool OnlyVirtualSpecified = HasVirtual && !HasOverride && !HasFinal;
79 unsigned KeywordCount = HasVirtual + HasOverride + HasFinal;
81 if (!OnlyVirtualSpecified && KeywordCount == 1)
86 if (OnlyVirtualSpecified) {
88 "prefer using 'override' or (rarely) 'final' instead of 'virtual'";
89 }
else if (KeywordCount == 0) {
90 Message =
"annotate this function with 'override' or (rarely) 'final'";
93 HasVirtual ? (HasOverride && HasFinal ?
"'virtual' and 'override' are"
96 StringRef Correct = HasFinal ?
"'final'" :
"'override'";
98 Message = (llvm::Twine(Redundant) +
99 " redundant since the function is already declared " + Correct)
103 DiagnosticBuilder Diag = diag(Method->getLocation(),
Message);
105 CharSourceRange FileRange = Lexer::makeFileCharRange(
106 CharSourceRange::getTokenRange(Method->getSourceRange()), Sources,
109 if (!FileRange.isValid())
118 if (!HasFinal && !HasOverride) {
119 SourceLocation InsertLoc;
120 StringRef ReplacementText =
"override ";
121 SourceLocation MethodLoc = Method->getLocation();
123 for (Token T : Tokens) {
124 if (T.is(tok::kw___attribute) &&
125 !Sources.isBeforeInTranslationUnit(T.getLocation(), MethodLoc)) {
126 InsertLoc = T.getLocation();
131 if (Method->hasAttrs()) {
132 for (
const clang::Attr *A : Method->getAttrs()) {
133 if (!A->isImplicit() && !A->isInherited()) {
135 Sources.getExpansionLoc(A->getRange().getBegin());
136 if ((!InsertLoc.isValid() ||
137 Sources.isBeforeInTranslationUnit(Loc, InsertLoc)) &&
138 !Sources.isBeforeInTranslationUnit(Loc, MethodLoc))
144 if (InsertLoc.isInvalid() && Method->doesThisDeclarationHaveABody() &&
145 Method->getBody() && !Method->isDefaulted()) {
150 Token LastNonCommentToken;
151 for (Token T : Tokens) {
152 if (!T.is(tok::comment)) {
153 LastNonCommentToken = T;
156 InsertLoc = LastNonCommentToken.getEndLoc();
157 ReplacementText =
" override";
160 if (!InsertLoc.isValid()) {
164 if (Tokens.size() > 2 && (
GetText(Tokens.back(), Sources) ==
"0" ||
165 Tokens.back().is(tok::kw_default) ||
166 Tokens.back().is(tok::kw_delete)) &&
167 GetText(Tokens[Tokens.size() - 2], Sources) ==
"=") {
168 InsertLoc = Tokens[Tokens.size() - 2].getLocation();
170 if ((Tokens[Tokens.size() - 2].getFlags() & Token::LeadingSpace) == 0)
171 ReplacementText =
" override ";
172 }
else if (
GetText(Tokens.back(), Sources) ==
"ABSTRACT") {
173 InsertLoc = Tokens.back().getLocation();
177 if (!InsertLoc.isValid()) {
178 InsertLoc = FileRange.getEnd();
179 ReplacementText =
" override";
181 Diag << FixItHint::CreateInsertion(InsertLoc, ReplacementText);
184 if (HasFinal && HasOverride) {
185 SourceLocation OverrideLoc = Method->getAttr<OverrideAttr>()->getLocation();
186 Diag << FixItHint::CreateRemoval(
187 CharSourceRange::getTokenRange(OverrideLoc, OverrideLoc));
191 for (Token Tok : Tokens) {
192 if (Tok.is(tok::kw_virtual)) {
193 Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
194 Tok.getLocation(), Tok.getLocation()));
SourceLocation Loc
'#' location in the include directive
std::unique_ptr< ast_matchers::MatchFinder > Finder
static const StringRef Message
static StringRef GetText(const Token &Tok, const SourceManager &Sources)
CharSourceRange Range
SourceRange for the file name.
static SmallVector< Token, 16 > ParseTokens(CharSourceRange Range, const MatchFinder::MatchResult &Result)