10#include "../utils/LexerUtils.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Lex/Lexer.h"
21 IgnoreDestructors(Options.get(
"IgnoreDestructors", false)),
22 IgnoreTemplateInstantiations(
23 Options.get(
"IgnoreTemplateInstantiations", false)),
24 AllowOverrideAndFinal(Options.get(
"AllowOverrideAndFinal", false)),
25 OverrideSpelling(Options.get(
"OverrideSpelling",
"override")),
26 FinalSpelling(Options.get(
"FinalSpelling",
"final")) {}
29 Options.
store(Opts,
"IgnoreDestructors", IgnoreDestructors);
31 IgnoreTemplateInstantiations);
32 Options.
store(Opts,
"AllowOverrideAndFinal", AllowOverrideAndFinal);
33 Options.
store(Opts,
"OverrideSpelling", OverrideSpelling);
39 auto IgnoreDestructorMatcher =
40 IgnoreDestructors ? cxxMethodDecl(unless(cxxDestructorDecl()))
42 auto IgnoreTemplateInstantiationsMatcher =
43 IgnoreTemplateInstantiations
44 ? cxxMethodDecl(unless(ast_matchers::isTemplateInstantiation()))
46 Finder->addMatcher(cxxMethodDecl(isOverride(),
47 IgnoreTemplateInstantiationsMatcher,
48 IgnoreDestructorMatcher)
55static SmallVector<Token, 16>
56parseTokens(CharSourceRange Range,
const MatchFinder::MatchResult &Result) {
57 const SourceManager &Sources = *Result.SourceManager;
58 std::pair<FileID, unsigned> LocInfo =
59 Sources.getDecomposedLoc(
Range.getBegin());
60 StringRef File = Sources.getBufferData(LocInfo.first);
61 const char *TokenBegin = File.data() + LocInfo.second;
62 Lexer RawLexer(Sources.getLocForStartOfFile(LocInfo.first),
63 Result.Context->getLangOpts(), File.begin(), TokenBegin,
65 SmallVector<Token, 16> Tokens;
68 while (!RawLexer.LexFromRawLexer(Tok)) {
69 if ((Tok.is(tok::semi) || Tok.is(tok::l_brace)) && NestedParens == 0)
71 if (Sources.isBeforeInTranslationUnit(
Range.getEnd(), Tok.getLocation()))
73 if (Tok.is(tok::l_paren))
75 else if (Tok.is(tok::r_paren))
77 if (Tok.is(tok::raw_identifier)) {
78 IdentifierInfo &
Info = Result.Context->Idents.get(StringRef(
79 Sources.getCharacterData(Tok.getLocation()), Tok.getLength()));
80 Tok.setIdentifierInfo(&
Info);
81 Tok.setKind(
Info.getTokenID());
83 Tokens.push_back(Tok);
88static StringRef
getText(
const Token &Tok,
const SourceManager &Sources) {
89 return {Sources.getCharacterData(Tok.getLocation()), Tok.getLength()};
93 const auto *Method = Result.Nodes.getNodeAs<FunctionDecl>(
"method");
94 const SourceManager &Sources = *Result.SourceManager;
96 ASTContext &Context = *Result.Context;
98 assert(Method !=
nullptr);
99 if (Method->getInstantiatedFromMemberFunction() !=
nullptr)
100 Method = Method->getInstantiatedFromMemberFunction();
102 if (Method->isImplicit() || Method->getLocation().isMacroID() ||
103 Method->isOutOfLine())
106 bool HasVirtual = Method->isVirtualAsWritten();
107 bool HasOverride = Method->getAttr<OverrideAttr>();
108 bool HasFinal = Method->getAttr<FinalAttr>();
110 bool OnlyVirtualSpecified = HasVirtual && !HasOverride && !HasFinal;
111 unsigned KeywordCount = HasVirtual + HasOverride + HasFinal;
113 if ((!OnlyVirtualSpecified && KeywordCount == 1) ||
114 (!HasVirtual && HasOverride && HasFinal && AllowOverrideAndFinal))
118 if (OnlyVirtualSpecified) {
119 Message =
"prefer using '%0' or (rarely) '%1' instead of 'virtual'";
120 }
else if (KeywordCount == 0) {
121 Message =
"annotate this function with '%0' or (rarely) '%1'";
123 StringRef Redundant =
124 HasVirtual ? (HasOverride && HasFinal && !AllowOverrideAndFinal
125 ?
"'virtual' and '%0' are"
128 StringRef Correct = HasFinal ?
"'%1'" :
"'%0'";
130 Message = (llvm::Twine(Redundant) +
131 " redundant since the function is already declared " + Correct)
136 << OverrideSpelling << FinalSpelling;
138 CharSourceRange FileRange = Lexer::makeFileCharRange(
139 CharSourceRange::getTokenRange(Method->getSourceRange()), Sources,
142 if (!FileRange.isValid())
148 SmallVector<Token, 16> Tokens =
parseTokens(FileRange, Result);
151 if (!HasFinal && !HasOverride) {
152 SourceLocation InsertLoc;
153 std::string ReplacementText = (OverrideSpelling +
" ").str();
154 SourceLocation MethodLoc = Method->getLocation();
156 for (Token T : Tokens) {
157 if (T.is(tok::kw___attribute) &&
158 !Sources.isBeforeInTranslationUnit(T.getLocation(), MethodLoc)) {
159 InsertLoc = T.getLocation();
164 if (Method->hasAttrs()) {
165 for (
const clang::Attr *A : Method->getAttrs()) {
166 if (!A->isImplicit() && !A->isInherited()) {
168 Sources.getExpansionLoc(A->getRange().getBegin());
169 if ((!InsertLoc.isValid() ||
170 Sources.isBeforeInTranslationUnit(
Loc, InsertLoc)) &&
171 !Sources.isBeforeInTranslationUnit(
Loc, MethodLoc))
177 if (InsertLoc.isInvalid() && Method->doesThisDeclarationHaveABody() &&
178 Method->getBody() && !Method->isDefaulted()) {
183 ReplacementText = (
" " + OverrideSpelling).str();
184 auto *LastTokenIter = std::prev(Tokens.end());
187 if (LastTokenIter->is(tok::kw_try))
188 LastTokenIter = std::prev(LastTokenIter);
189 InsertLoc = LastTokenIter->getEndLoc();
192 if (!InsertLoc.isValid()) {
196 if (Tokens.size() > 2 &&
197 (
getText(Tokens.back(), Sources) ==
"0" ||
198 Tokens.back().is(tok::kw_default) ||
199 Tokens.back().is(tok::kw_delete)) &&
200 getText(Tokens[Tokens.size() - 2], Sources) ==
"=") {
201 InsertLoc = Tokens[Tokens.size() - 2].getLocation();
203 if ((Tokens[Tokens.size() - 2].getFlags() & Token::LeadingSpace) == 0)
204 ReplacementText = (
" " + OverrideSpelling +
" ").str();
205 }
else if (
getText(Tokens.back(), Sources) ==
"ABSTRACT")
206 InsertLoc = Tokens.back().getLocation();
209 if (!InsertLoc.isValid()) {
210 InsertLoc = FileRange.getEnd();
211 ReplacementText = (
" " + OverrideSpelling).str();
216 if (OverrideSpelling !=
"override" &&
217 !Context.Idents.get(OverrideSpelling).hasMacroDefinition())
220 Diag << FixItHint::CreateInsertion(InsertLoc, ReplacementText);
223 if (HasFinal && HasOverride && !AllowOverrideAndFinal) {
224 SourceLocation OverrideLoc = Method->getAttr<OverrideAttr>()->getLocation();
225 Diag << FixItHint::CreateRemoval(
226 CharSourceRange::getTokenRange(OverrideLoc, OverrideLoc));
230 for (Token Tok : Tokens) {
231 if (Tok.is(tok::kw_virtual)) {
232 std::optional<Token> NextToken =
235 if (NextToken.has_value()) {
236 Diag << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
237 Tok.getLocation(), NextToken->getLocation()));
llvm::SmallString< 256U > Name
CharSourceRange Range
SourceRange for the file name.
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
const LangOptions & getLangOpts() const
Returns the language options from the context.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
UseOverrideCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
static StringRef getText(const Token &Tok, const SourceManager &Sources)
static SmallVector< Token, 16 > parseTokens(CharSourceRange Range, const MatchFinder::MatchResult &Result)
constexpr llvm::StringLiteral Message
std::optional< Token > findNextTokenIncludingComments(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts)
llvm::StringMap< ClangTidyValue > OptionMap