clang-tools 6.0.0

UseOverrideCheck.cpp
Go to the documentation of this file.
1 //===--- UseOverrideCheck.cpp - clang-tidy --------------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "UseOverrideCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.h"
14
15 using namespace clang::ast_matchers;
16
17 namespace clang {
18 namespace tidy {
19 namespace modernize {
20
21 void UseOverrideCheck::registerMatchers(MatchFinder *Finder) {
22 // Only register the matcher for C++11.
23 if (getLangOpts().CPlusPlus11)
24 Finder->addMatcher(cxxMethodDecl(isOverride()).bind("method"), this);
25 }
26
27 // Re-lex the tokens to get precise locations to insert 'override' and remove
28 // 'virtual'.
29 static SmallVector<Token, 16>
30 ParseTokens(CharSourceRange Range, const MatchFinder::MatchResult &Result) {
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,
38 File.end());
39 SmallVector<Token, 16> Tokens;
40 Token Tok;
41 int NestedParens = 0;
42 while (!RawLexer.LexFromRawLexer(Tok)) {
43 if ((Tok.is(tok::semi) || Tok.is(tok::l_brace)) && NestedParens == 0)
44 break;
45 if (Sources.isBeforeInTranslationUnit(Range.getEnd(), Tok.getLocation()))
46 break;
47 if (Tok.is(tok::l_paren))
48 ++NestedParens;
49 else if (Tok.is(tok::r_paren))
50 --NestedParens;
51 if (Tok.is(tok::raw_identifier)) {
52 IdentifierInfo &Info = Result.Context->Idents.get(StringRef(
53 Sources.getCharacterData(Tok.getLocation()), Tok.getLength()));
54 Tok.setIdentifierInfo(&Info);
55 Tok.setKind(Info.getTokenID());
56 }
57 Tokens.push_back(Tok);
58 }
59 return Tokens;
60 }
61
62 static StringRef GetText(const Token &Tok, const SourceManager &Sources) {
63 return StringRef(Sources.getCharacterData(Tok.getLocation()),
64 Tok.getLength());
65 }
66
67 void UseOverrideCheck::check(const MatchFinder::MatchResult &Result) {
68 const auto *Method = Result.Nodes.getNodeAs<FunctionDecl>("method");
69 const SourceManager &Sources = *Result.SourceManager;
70
71 assert(Method != nullptr);
72 if (Method->getInstantiatedFromMemberFunction() != nullptr)
73 Method = Method->getInstantiatedFromMemberFunction();
74
75 if (Method->isImplicit() || Method->getLocation().isMacroID() ||
76 Method->isOutOfLine())
77 return;
78
79 bool HasVirtual = Method->isVirtualAsWritten();
80 bool HasOverride = Method->getAttr<OverrideAttr>();
81 bool HasFinal = Method->getAttr<FinalAttr>();
82
83 bool OnlyVirtualSpecified = HasVirtual && !HasOverride && !HasFinal;
84 unsigned KeywordCount = HasVirtual + HasOverride + HasFinal;
85
86 if (!OnlyVirtualSpecified && KeywordCount == 1)
87 return; // Nothing to do.
88
89 std::string Message;
90
91 if (OnlyVirtualSpecified) {
92 Message =
93 "prefer using 'override' or (rarely) 'final' instead of 'virtual'";
94 } else if (KeywordCount == 0) {
95 Message = "annotate this function with 'override' or (rarely) 'final'";
96 } else {
97 StringRef Redundant =
98 HasVirtual ? (HasOverride && HasFinal ? "'virtual' and 'override' are"
99 : "'virtual' is")
100 : "'override' is";
101 StringRef Correct = HasFinal ? "'final'" : "'override'";
102
103 Message = (llvm::Twine(Redundant) +
104 " redundant since the function is already declared " + Correct)
105 .str();
106 }
107
108 DiagnosticBuilder Diag = diag(Method->getLocation(), Message);
109
110 CharSourceRange FileRange = Lexer::makeFileCharRange(
111 CharSourceRange::getTokenRange(Method->getSourceRange()), Sources,
112 getLangOpts());
113
114 if (!FileRange.isValid())
115 return;
116
117 // FIXME: Instead of re-lexing and looking for specific macros such as
118 // 'ABSTRACT', properly store the location of 'virtual' and '= 0' in each
119 // FunctionDecl.
120 SmallVector<Token, 16> Tokens = ParseTokens(FileRange, Result);
121
122 // Add 'override' on inline declarations that don't already have it.
123 if (!HasFinal && !HasOverride) {
124 SourceLocation InsertLoc;
125 StringRef ReplacementText = "override ";
126 SourceLocation MethodLoc = Method->getLocation();
127
128 for (Token T : Tokens) {
129 if (T.is(tok::kw___attribute) &&
130 !Sources.isBeforeInTranslationUnit(T.getLocation(), MethodLoc)) {
131 InsertLoc = T.getLocation();
132 break;
133 }
134 }
135
136 if (Method->hasAttrs()) {
137 for (const clang::Attr *A : Method->getAttrs()) {
138 if (!A->isImplicit() && !A->isInherited()) {
139 SourceLocation Loc =
140 Sources.getExpansionLoc(A->getRange().getBegin());
141 if ((!InsertLoc.isValid() ||
142 Sources.isBeforeInTranslationUnit(Loc, InsertLoc)) &&
143 !Sources.isBeforeInTranslationUnit(Loc, MethodLoc))
144 InsertLoc = Loc;
145 }
146 }
147 }
148
149 if (InsertLoc.isInvalid() && Method->doesThisDeclarationHaveABody() &&
150 Method->getBody() && !Method->isDefaulted()) {
151 // For methods with inline definition, add the override keyword at the
152 // end of the declaration of the function, but prefer to put it on the
153 // same line as the declaration if the beginning brace for the start of
154 // the body falls on the next line.
155 ReplacementText = " override";
156 auto LastTokenIter = std::prev(Tokens.end());
157 // When try statement is used instead of compound statement as
158 // method body - insert override keyword before it.
159 if (LastTokenIter->is(tok::kw_try))
160 LastTokenIter = std::prev(LastTokenIter);
161 InsertLoc = LastTokenIter->getEndLoc();
162 }
163
164 if (!InsertLoc.isValid()) {
165 // For declarations marked with "= 0" or "= [default|delete]", the end
166 // location will point until after those markings. Therefore, the override
167 // keyword shouldn't be inserted at the end, but before the '='.
168 if (Tokens.size() > 2 && (GetText(Tokens.back(), Sources) == "0" ||
169 Tokens.back().is(tok::kw_default) ||
170 Tokens.back().is(tok::kw_delete)) &&
171 GetText(Tokens[Tokens.size() - 2], Sources) == "=") {
172 InsertLoc = Tokens[Tokens.size() - 2].getLocation();
173 // Check if we need to insert a space.
174 if ((Tokens[Tokens.size() - 2].getFlags() & Token::LeadingSpace) == 0)
175 ReplacementText = " override ";
176 } else if (GetText(Tokens.back(), Sources) == "ABSTRACT") {
177 InsertLoc = Tokens.back().getLocation();
178 }
179 }
180
181 if (!InsertLoc.isValid()) {
182 InsertLoc = FileRange.getEnd();
183 ReplacementText = " override";
184 }
185 Diag << FixItHint::CreateInsertion(InsertLoc, ReplacementText);
186 }
187
188 if (HasFinal && HasOverride) {
189 SourceLocation OverrideLoc = Method->getAttr<OverrideAttr>()->getLocation();
190 Diag << FixItHint::CreateRemoval(
191 CharSourceRange::getTokenRange(OverrideLoc, OverrideLoc));
192 }
193
194 if (HasVirtual) {
195 for (Token Tok : Tokens) {
196 if (Tok.is(tok::kw_virtual)) {
197 Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
198 Tok.getLocation(), Tok.getLocation()));
199 break;
200 }
201 }
202 }
203 }
204
205 } // namespace modernize
206 } // namespace tidy
207 } // namespace clang
Loc
SourceLocation Loc
&#39;#&#39; location in the include directive
Definition: IncludeOrderCheck.cpp:36
clang::ast_matchers
Definition: ClangTidyDiagnosticConsumer.h:27
File
HeaderHandle File
Definition: PreprocessorTracker.cpp:526
clang::tidy::modernize::Message
static const StringRef Message
Definition: UseTransparentFunctorsCheck.cpp:71
UseOverrideCheck.h
Tokens
StringRef Tokens
Definition: AvoidBindCheck.cpp:40
Info
FunctionInfo Info
Definition: FunctionSizeCheck.cpp:84
clang::tidy::modernize::GetText
static StringRef GetText(const Token &Tok, const SourceManager &Sources)
Definition: UseOverrideCheck.cpp:62
clang
Definition: AndroidTidyModule.cpp:28
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:37
clang::tidy::modernize::ParseTokens
static SmallVector< Token, 16 > ParseTokens(CharSourceRange Range, const MatchFinder::MatchResult &Result)
Definition: UseOverrideCheck.cpp:30