clang-tools 10.0.0

UseOverrideCheck.cpp
Go to the documentation of this file.
1 //===--- UseOverrideCheck.cpp - clang-tidy --------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "UseOverrideCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Lex/Lexer.h"
13
14 using namespace clang::ast_matchers;
15
16 namespace clang {
17 namespace tidy {
18 namespace modernize {
19
20 UseOverrideCheck::UseOverrideCheck(StringRef Name, ClangTidyContext *Context)
21 : ClangTidyCheck(Name, Context),
22 IgnoreDestructors(Options.get("IgnoreDestructors", false)),
23 AllowOverrideAndFinal(Options.get("AllowOverrideAndFinal", false)),
24 OverrideSpelling(Options.get("OverrideSpelling", "override")),
25 FinalSpelling(Options.get("FinalSpelling", "final")) {}
26
27 void UseOverrideCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
28 Options.store(Opts, "IgnoreDestructors", IgnoreDestructors);
29 Options.store(Opts, "AllowOverrideAndFinal", AllowOverrideAndFinal);
30 Options.store(Opts, "OverrideSpelling", OverrideSpelling);
31 Options.store(Opts, "FinalSpelling", FinalSpelling);
32 }
33
34 void UseOverrideCheck::registerMatchers(MatchFinder *Finder) {
35 // Only register the matcher for C++11.
36 if (!getLangOpts().CPlusPlus11)
37 return;
38
39 if (IgnoreDestructors)
40 Finder->addMatcher(
41 cxxMethodDecl(isOverride(), unless(cxxDestructorDecl())).bind("method"),
42 this);
43 else
44 Finder->addMatcher(cxxMethodDecl(isOverride()).bind("method"), this);
45 }
46
47 // Re-lex the tokens to get precise locations to insert 'override' and remove
48 // 'virtual'.
49 static SmallVector<Token, 16>
50 ParseTokens(CharSourceRange Range, const MatchFinder::MatchResult &Result) {
51 const SourceManager &Sources = *Result.SourceManager;
52 std::pair<FileID, unsigned> LocInfo =
53 Sources.getDecomposedLoc(Range.getBegin());
54 StringRef File = Sources.getBufferData(LocInfo.first);
55 const char *TokenBegin = File.data() + LocInfo.second;
56 Lexer RawLexer(Sources.getLocForStartOfFile(LocInfo.first),
57 Result.Context->getLangOpts(), File.begin(), TokenBegin,
58 File.end());
59 SmallVector<Token, 16> Tokens;
60 Token Tok;
61 int NestedParens = 0;
62 while (!RawLexer.LexFromRawLexer(Tok)) {
63 if ((Tok.is(tok::semi) || Tok.is(tok::l_brace)) && NestedParens == 0)
64 break;
65 if (Sources.isBeforeInTranslationUnit(Range.getEnd(), Tok.getLocation()))
66 break;
67 if (Tok.is(tok::l_paren))
68 ++NestedParens;
69 else if (Tok.is(tok::r_paren))
70 --NestedParens;
71 if (Tok.is(tok::raw_identifier)) {
72 IdentifierInfo &Info = Result.Context->Idents.get(StringRef(
73 Sources.getCharacterData(Tok.getLocation()), Tok.getLength()));
74 Tok.setIdentifierInfo(&Info);
75 Tok.setKind(Info.getTokenID());
76 }
77 Tokens.push_back(Tok);
78 }
79 return Tokens;
80 }
81
82 static StringRef GetText(const Token &Tok, const SourceManager &Sources) {
83 return StringRef(Sources.getCharacterData(Tok.getLocation()),
84 Tok.getLength());
85 }
86
87 void UseOverrideCheck::check(const MatchFinder::MatchResult &Result) {
88 const auto *Method = Result.Nodes.getNodeAs<FunctionDecl>("method");
89 const SourceManager &Sources = *Result.SourceManager;
90
91 ASTContext &Context = *Result.Context;
92
93 assert(Method != nullptr);
94 if (Method->getInstantiatedFromMemberFunction() != nullptr)
95 Method = Method->getInstantiatedFromMemberFunction();
96
97 if (Method->isImplicit() || Method->getLocation().isMacroID() ||
98 Method->isOutOfLine())
99 return;
100
101 bool HasVirtual = Method->isVirtualAsWritten();
102 bool HasOverride = Method->getAttr<OverrideAttr>();
103 bool HasFinal = Method->getAttr<FinalAttr>();
104
105 bool OnlyVirtualSpecified = HasVirtual && !HasOverride && !HasFinal;
106 unsigned KeywordCount = HasVirtual + HasOverride + HasFinal;
107
108 if ((!OnlyVirtualSpecified && KeywordCount == 1) ||
109 (!HasVirtual && HasOverride && HasFinal && AllowOverrideAndFinal))
110 return; // Nothing to do.
111
112 std::string Message;
113 if (OnlyVirtualSpecified) {
114 Message = "prefer using '%0' or (rarely) '%1' instead of 'virtual'";
115 } else if (KeywordCount == 0) {
116 Message = "annotate this function with '%0' or (rarely) '%1'";
117 } else {
118 StringRef Redundant =
119 HasVirtual ? (HasOverride && HasFinal && !AllowOverrideAndFinal
120 ? "'virtual' and '%0' are"
121 : "'virtual' is")
122 : "'%0' is";
123 StringRef Correct = HasFinal ? "'%1'" : "'%0'";
124
125 Message = (llvm::Twine(Redundant) +
126 " redundant since the function is already declared " + Correct)
127 .str();
128 }
129
130 auto Diag = diag(Method->getLocation(), Message)
131 << OverrideSpelling << FinalSpelling;
132
133 CharSourceRange FileRange = Lexer::makeFileCharRange(
134 CharSourceRange::getTokenRange(Method->getSourceRange()), Sources,
135 getLangOpts());
136
137 if (!FileRange.isValid())
138 return;
139
140 // FIXME: Instead of re-lexing and looking for specific macros such as
141 // 'ABSTRACT', properly store the location of 'virtual' and '= 0' in each
142 // FunctionDecl.
143 SmallVector<Token, 16> Tokens = ParseTokens(FileRange, Result);
144
145 // Add 'override' on inline declarations that don't already have it.
146 if (!HasFinal && !HasOverride) {
147 SourceLocation InsertLoc;
148 std::string ReplacementText = OverrideSpelling + " ";
149 SourceLocation MethodLoc = Method->getLocation();
150
151 for (Token T : Tokens) {
152 if (T.is(tok::kw___attribute) &&
153 !Sources.isBeforeInTranslationUnit(T.getLocation(), MethodLoc)) {
154 InsertLoc = T.getLocation();
155 break;
156 }
157 }
158
159 if (Method->hasAttrs()) {
160 for (const clang::Attr *A : Method->getAttrs()) {
161 if (!A->isImplicit() && !A->isInherited()) {
162 SourceLocation Loc =
163 Sources.getExpansionLoc(A->getRange().getBegin());
164 if ((!InsertLoc.isValid() ||
165 Sources.isBeforeInTranslationUnit(Loc, InsertLoc)) &&
166 !Sources.isBeforeInTranslationUnit(Loc, MethodLoc))
167 InsertLoc = Loc;
168 }
169 }
170 }
171
172 if (InsertLoc.isInvalid() && Method->doesThisDeclarationHaveABody() &&
173 Method->getBody() && !Method->isDefaulted()) {
174 // For methods with inline definition, add the override keyword at the
175 // end of the declaration of the function, but prefer to put it on the
176 // same line as the declaration if the beginning brace for the start of
177 // the body falls on the next line.
178 ReplacementText = " " + OverrideSpelling;
179 auto LastTokenIter = std::prev(Tokens.end());
180 // When try statement is used instead of compound statement as
181 // method body - insert override keyword before it.
182 if (LastTokenIter->is(tok::kw_try))
183 LastTokenIter = std::prev(LastTokenIter);
184 InsertLoc = LastTokenIter->getEndLoc();
185 }
186
187 if (!InsertLoc.isValid()) {
188 // For declarations marked with "= 0" or "= [default|delete]", the end
189 // location will point until after those markings. Therefore, the override
190 // keyword shouldn't be inserted at the end, but before the '='.
191 if (Tokens.size() > 2 &&
192 (GetText(Tokens.back(), Sources) == "0" ||
193 Tokens.back().is(tok::kw_default) ||
194 Tokens.back().is(tok::kw_delete)) &&
195 GetText(Tokens[Tokens.size() - 2], Sources) == "=") {
196 InsertLoc = Tokens[Tokens.size() - 2].getLocation();
197 // Check if we need to insert a space.
198 if ((Tokens[Tokens.size() - 2].getFlags() & Token::LeadingSpace) == 0)
199 ReplacementText = " " + OverrideSpelling + " ";
200 } else if (GetText(Tokens.back(), Sources) == "ABSTRACT")
201 InsertLoc = Tokens.back().getLocation();
202 }
203
204 if (!InsertLoc.isValid()) {
205 InsertLoc = FileRange.getEnd();
206 ReplacementText = " " + OverrideSpelling;
207 }
208
209 // If the override macro has been specified just ensure it exists,
210 // if not don't apply a fixit but keep the warning.
211 if (OverrideSpelling != "override" &&
212 !Context.Idents.get(OverrideSpelling).hasMacroDefinition())
213 return;
214
215 Diag << FixItHint::CreateInsertion(InsertLoc, ReplacementText);
216 }
217
218 if (HasFinal && HasOverride && !AllowOverrideAndFinal) {
219 SourceLocation OverrideLoc = Method->getAttr<OverrideAttr>()->getLocation();
220 Diag << FixItHint::CreateRemoval(
221 CharSourceRange::getTokenRange(OverrideLoc, OverrideLoc));
222 }
223
224 if (HasVirtual) {
225 for (Token Tok : Tokens) {
226 if (Tok.is(tok::kw_virtual)) {
227 Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
228 Tok.getLocation(), Tok.getLocation()));
229 break;
230 }
231 }
232 }
233 }
234
235 } // namespace modernize
236 } // namespace tidy
237 } // namespace clang
Loc
SourceLocation Loc
&#39;#&#39; location in the include directive
Definition: IncludeOrderCheck.cpp:37
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::tidy::modernize::Message
constexpr llvm::StringLiteral Message
Definition: UseTrailingReturnTypeCheck.cpp:101
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:47
clang::tidy::modernize::UseOverrideCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: UseOverrideCheck.cpp:34
clang::tidy::ClangTidyCheck::getLangOpts
const LangOptions & getLangOpts() const
Returns the language options from the context.
Definition: ClangTidyCheck.h:188
clang::tidy::modernize::UseOverrideCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: UseOverrideCheck.cpp:87
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:184
clang::tidy::modernize::UseOverrideCheck::storeOptions
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Definition: UseOverrideCheck.cpp:27
UseOverrideCheck.h
clang::tidy::ClangTidyCheck::OptionsView::store
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.
Definition: ClangTidyCheck.cpp:58
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, std::string > OptionMap
Definition: ClangTidyOptions.h:97
clang::clangd::getTokenRange
llvm::Optional< Range > getTokenRange(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation TokLoc)
Returns the taken range at TokLoc.
Definition: SourceCode.cpp:227
Info
FunctionInfo Info
Definition: FunctionSizeCheck.cpp:120
clang::tidy::modernize::GetText
static StringRef GetText(const Token &Tok, const SourceManager &Sources)
Definition: UseOverrideCheck.cpp:82
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:77
clang::tidy::modernize::ParseTokens
static SmallVector< Token, 16 > ParseTokens(CharSourceRange Range, const MatchFinder::MatchResult &Result)
Definition: UseOverrideCheck.cpp:50
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidyCheck.cpp:21