clang-tools  10.0.0git
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 
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
SourceLocation Loc
&#39;#&#39; location in the include directive
constexpr llvm::StringLiteral Message
Base class for all clang-tidy checks.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
const LangOptions & getLangOpts() const
Returns the language options from the context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
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.
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
llvm::Optional< Range > getTokenRange(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation TokLoc)
Returns the taken range at TokLoc.
Definition: SourceCode.cpp:227
FunctionInfo Info
static StringRef GetText(const Token &Tok, const SourceManager &Sources)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
CharSourceRange Range
SourceRange for the file name.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
static SmallVector< Token, 16 > ParseTokens(CharSourceRange Range, const MatchFinder::MatchResult &Result)
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.