clang-tools  8.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
SourceLocation Loc
&#39;#&#39; location in the include directive
StringRef Tokens
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
FunctionInfo Info
static StringRef GetText(const Token &Tok, const SourceManager &Sources)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
CharSourceRange Range
SourceRange for the file name.
static SmallVector< Token, 16 > ParseTokens(CharSourceRange Range, const MatchFinder::MatchResult &Result)