clang-tools 3.8.0

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

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 while (!RawLexer.LexFromRawLexer(Tok)) {
42 if (Tok.is(tok::semi) || Tok.is(tok::l_brace))
43 break;
44 if (Sources.isBeforeInTranslationUnit(Range.getEnd(), Tok.getLocation()))
45 break;
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());
51 }
52 Tokens.push_back(Tok);
53 }
54 return Tokens;
55 }
56
57 static StringRef GetText(const Token &Tok, const SourceManager &Sources) {
58 return StringRef(Sources.getCharacterData(Tok.getLocation()),
59 Tok.getLength());
60 }
61
62 void UseOverrideCheck::check(const MatchFinder::MatchResult &Result) {
63 const FunctionDecl *Method = Result.Nodes.getStmtAs<FunctionDecl>("method");
64 const SourceManager &Sources = *Result.SourceManager;
65
66 assert(Method != nullptr);
67 if (Method->getInstantiatedFromMemberFunction() != nullptr)
68 Method = Method->getInstantiatedFromMemberFunction();
69
70 if (Method->isImplicit() || Method->getLocation().isMacroID() ||
71 Method->isOutOfLine())
72 return;
73
74 bool HasVirtual = Method->isVirtualAsWritten();
75 bool HasOverride = Method->getAttr<OverrideAttr>();
76 bool HasFinal = Method->getAttr<FinalAttr>();
77
78 bool OnlyVirtualSpecified = HasVirtual && !HasOverride && !HasFinal;
79 unsigned KeywordCount = HasVirtual + HasOverride + HasFinal;
80
81 if (!OnlyVirtualSpecified && KeywordCount == 1)
82 return; // Nothing to do.
83
84 std::string Message;
85
86 if (OnlyVirtualSpecified) {
87 Message =
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'";
91 } else {
92 StringRef Redundant =
93 HasVirtual ? (HasOverride && HasFinal ? "'virtual' and 'override' are"
94 : "'virtual' is")
95 : "'override' is";
96 StringRef Correct = HasFinal ? "'final'" : "'override'";
97
98 Message =
99 (llvm::Twine(Redundant) +
100 " redundant since the function is already declared " + Correct).str();
101 }
102
103 DiagnosticBuilder Diag = diag(Method->getLocation(), Message);
104
105 CharSourceRange FileRange = Lexer::makeFileCharRange(
106 CharSourceRange::getTokenRange(Method->getSourceRange()), Sources,
107 Result.Context->getLangOpts());
108
109 if (!FileRange.isValid())
110 return;
111
112 // FIXME: Instead of re-lexing and looking for specific macros such as
113 // 'ABSTRACT', properly store the location of 'virtual' and '= 0' in each
114 // FunctionDecl.
115 SmallVector<Token, 16> Tokens = ParseTokens(FileRange, Result);
116
117 // Add 'override' on inline declarations that don't already have it.
118 if (!HasFinal && !HasOverride) {
119 SourceLocation InsertLoc;
120 StringRef ReplacementText = "override ";
121
122 for (Token T : Tokens) {
123 if (T.is(tok::kw___attribute)) {
124 InsertLoc = T.getLocation();
125 break;
126 }
127 }
128
129 if (Method->hasAttrs()) {
130 for (const clang::Attr *A : Method->getAttrs()) {
131 if (!A->isImplicit()) {
132 SourceLocation Loc =
133 Sources.getExpansionLoc(A->getRange().getBegin());
134 if (!InsertLoc.isValid() ||
135 Sources.isBeforeInTranslationUnit(Loc, InsertLoc))
136 InsertLoc = Loc;
137 }
138 }
139 }
140
141 if (InsertLoc.isInvalid() && Method->doesThisDeclarationHaveABody() &&
142 Method->getBody() && !Method->isDefaulted()) {
143 // For methods with inline definition, add the override keyword at the
144 // end of the declaration of the function, but prefer to put it on the
145 // same line as the declaration if the beginning brace for the start of
146 // the body falls on the next line.
147 Token LastNonCommentToken;
148 for (Token T : Tokens) {
149 if (!T.is(tok::comment)) {
150 LastNonCommentToken = T;
151 }
152 }
153 InsertLoc = LastNonCommentToken.getEndLoc();
154 ReplacementText = " override";
155 }
156
157 if (!InsertLoc.isValid()) {
158 // For declarations marked with "= 0" or "= [default|delete]", the end
159 // location will point until after those markings. Therefore, the override
160 // keyword shouldn't be inserted at the end, but before the '='.
161 if (Tokens.size() > 2 && (GetText(Tokens.back(), Sources) == "0" ||
162 Tokens.back().is(tok::kw_default) ||
163 Tokens.back().is(tok::kw_delete)) &&
164 GetText(Tokens[Tokens.size() - 2], Sources) == "=") {
165 InsertLoc = Tokens[Tokens.size() - 2].getLocation();
166 } else if (GetText(Tokens.back(), Sources) == "ABSTRACT") {
167 InsertLoc = Tokens.back().getLocation();
168 }
169 }
170
171 if (!InsertLoc.isValid()) {
172 InsertLoc = FileRange.getEnd();
173 ReplacementText = " override";
174 }
175 Diag << FixItHint::CreateInsertion(InsertLoc, ReplacementText);
176 }
177
178 if (HasFinal && HasOverride) {
179 SourceLocation OverrideLoc = Method->getAttr<OverrideAttr>()->getLocation();
180 Diag << FixItHint::CreateRemoval(
181 CharSourceRange::getTokenRange(OverrideLoc, OverrideLoc));
182 }
183
184 if (HasVirtual) {
185 for (Token Tok : Tokens) {
186 if (Tok.is(tok::kw_virtual)) {
187 Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
188 Tok.getLocation(), Tok.getLocation()));
189 break;
190 }
191 }
192 }
193 }
194
195 } // namespace modernize
196 } // namespace tidy
197 } // namespace clang
Loc
SourceLocation Loc
'#' location in the include directive
Definition: IncludeOrderCheck.cpp:34
Finder
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:188
File
HeaderHandle File
Definition: PreprocessorTracker.cpp:526
UseOverrideCheck.h
clang::tidy::modernize::GetText
static StringRef GetText(const Token &Tok, const SourceManager &Sources)
Definition: UseOverrideCheck.cpp:57
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:35
clang::tidy::modernize::ParseTokens
static SmallVector< Token, 16 > ParseTokens(CharSourceRange Range, const MatchFinder::MatchResult &Result)
Definition: UseOverrideCheck.cpp:30
Result
const NamedDecl * Result
Definition: USRFinder.cpp:121