UseOverrideCheck.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  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. #include "UseOverrideCheck.h"
  9. #include "clang/AST/ASTContext.h"
  10. #include "clang/ASTMatchers/ASTMatchFinder.h"
  11. #include "clang/Lex/Lexer.h"
  12. using namespace clang::ast_matchers;
  13. namespace clang::tidy::modernize {
  14. UseOverrideCheck::UseOverrideCheck(StringRef Name, ClangTidyContext *Context)
  15. : ClangTidyCheck(Name, Context),
  16. IgnoreDestructors(Options.get("IgnoreDestructors", false)),
  17. AllowOverrideAndFinal(Options.get("AllowOverrideAndFinal", false)),
  18. OverrideSpelling(Options.get("OverrideSpelling", "override")),
  19. FinalSpelling(Options.get("FinalSpelling", "final")) {}
  20. void UseOverrideCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
  21. Options.store(Opts, "IgnoreDestructors", IgnoreDestructors);
  22. Options.store(Opts, "AllowOverrideAndFinal", AllowOverrideAndFinal);
  23. Options.store(Opts, "OverrideSpelling", OverrideSpelling);
  24. Options.store(Opts, "FinalSpelling", FinalSpelling);
  25. }
  26. void UseOverrideCheck::registerMatchers(MatchFinder *Finder) {
  27. if (IgnoreDestructors)
  28. Finder->addMatcher(
  29. cxxMethodDecl(isOverride(), unless(cxxDestructorDecl())).bind("method"),
  30. this);
  31. else
  32. Finder->addMatcher(cxxMethodDecl(isOverride()).bind("method"), this);
  33. }
  34. // Re-lex the tokens to get precise locations to insert 'override' and remove
  35. // 'virtual'.
  36. static SmallVector<Token, 16>
  37. parseTokens(CharSourceRange Range, const MatchFinder::MatchResult &Result) {
  38. const SourceManager &Sources = *Result.SourceManager;
  39. std::pair<FileID, unsigned> LocInfo =
  40. Sources.getDecomposedLoc(Range.getBegin());
  41. StringRef File = Sources.getBufferData(LocInfo.first);
  42. const char *TokenBegin = File.data() + LocInfo.second;
  43. Lexer RawLexer(Sources.getLocForStartOfFile(LocInfo.first),
  44. Result.Context->getLangOpts(), File.begin(), TokenBegin,
  45. File.end());
  46. SmallVector<Token, 16> Tokens;
  47. Token Tok;
  48. int NestedParens = 0;
  49. while (!RawLexer.LexFromRawLexer(Tok)) {
  50. if ((Tok.is(tok::semi) || Tok.is(tok::l_brace)) && NestedParens == 0)
  51. break;
  52. if (Sources.isBeforeInTranslationUnit(Range.getEnd(), Tok.getLocation()))
  53. break;
  54. if (Tok.is(tok::l_paren))
  55. ++NestedParens;
  56. else if (Tok.is(tok::r_paren))
  57. --NestedParens;
  58. if (Tok.is(tok::raw_identifier)) {
  59. IdentifierInfo &Info = Result.Context->Idents.get(StringRef(
  60. Sources.getCharacterData(Tok.getLocation()), Tok.getLength()));
  61. Tok.setIdentifierInfo(&Info);
  62. Tok.setKind(Info.getTokenID());
  63. }
  64. Tokens.push_back(Tok);
  65. }
  66. return Tokens;
  67. }
  68. static StringRef getText(const Token &Tok, const SourceManager &Sources) {
  69. return StringRef(Sources.getCharacterData(Tok.getLocation()),
  70. Tok.getLength());
  71. }
  72. void UseOverrideCheck::check(const MatchFinder::MatchResult &Result) {
  73. const auto *Method = Result.Nodes.getNodeAs<FunctionDecl>("method");
  74. const SourceManager &Sources = *Result.SourceManager;
  75. ASTContext &Context = *Result.Context;
  76. assert(Method != nullptr);
  77. if (Method->getInstantiatedFromMemberFunction() != nullptr)
  78. Method = Method->getInstantiatedFromMemberFunction();
  79. if (Method->isImplicit() || Method->getLocation().isMacroID() ||
  80. Method->isOutOfLine())
  81. return;
  82. bool HasVirtual = Method->isVirtualAsWritten();
  83. bool HasOverride = Method->getAttr<OverrideAttr>();
  84. bool HasFinal = Method->getAttr<FinalAttr>();
  85. bool OnlyVirtualSpecified = HasVirtual && !HasOverride && !HasFinal;
  86. unsigned KeywordCount = HasVirtual + HasOverride + HasFinal;
  87. if ((!OnlyVirtualSpecified && KeywordCount == 1) ||
  88. (!HasVirtual && HasOverride && HasFinal && AllowOverrideAndFinal))
  89. return; // Nothing to do.
  90. std::string Message;
  91. if (OnlyVirtualSpecified) {
  92. Message = "prefer using '%0' or (rarely) '%1' instead of 'virtual'";
  93. } else if (KeywordCount == 0) {
  94. Message = "annotate this function with '%0' or (rarely) '%1'";
  95. } else {
  96. StringRef Redundant =
  97. HasVirtual ? (HasOverride && HasFinal && !AllowOverrideAndFinal
  98. ? "'virtual' and '%0' are"
  99. : "'virtual' is")
  100. : "'%0' is";
  101. StringRef Correct = HasFinal ? "'%1'" : "'%0'";
  102. Message = (llvm::Twine(Redundant) +
  103. " redundant since the function is already declared " + Correct)
  104. .str();
  105. }
  106. auto Diag = diag(Method->getLocation(), Message)
  107. << OverrideSpelling << FinalSpelling;
  108. CharSourceRange FileRange = Lexer::makeFileCharRange(
  109. CharSourceRange::getTokenRange(Method->getSourceRange()), Sources,
  110. getLangOpts());
  111. if (!FileRange.isValid())
  112. return;
  113. // FIXME: Instead of re-lexing and looking for specific macros such as
  114. // 'ABSTRACT', properly store the location of 'virtual' and '= 0' in each
  115. // FunctionDecl.
  116. SmallVector<Token, 16> Tokens = parseTokens(FileRange, Result);
  117. // Add 'override' on inline declarations that don't already have it.
  118. if (!HasFinal && !HasOverride) {
  119. SourceLocation InsertLoc;
  120. std::string ReplacementText = (OverrideSpelling + " ").str();
  121. SourceLocation MethodLoc = Method->getLocation();
  122. for (Token T : Tokens) {
  123. if (T.is(tok::kw___attribute) &&
  124. !Sources.isBeforeInTranslationUnit(T.getLocation(), MethodLoc)) {
  125. InsertLoc = T.getLocation();
  126. break;
  127. }
  128. }
  129. if (Method->hasAttrs()) {
  130. for (const clang::Attr *A : Method->getAttrs()) {
  131. if (!A->isImplicit() && !A->isInherited()) {
  132. SourceLocation Loc =
  133. Sources.getExpansionLoc(A->getRange().getBegin());
  134. if ((!InsertLoc.isValid() ||
  135. Sources.isBeforeInTranslationUnit(Loc, InsertLoc)) &&
  136. !Sources.isBeforeInTranslationUnit(Loc, MethodLoc))
  137. InsertLoc = Loc;
  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. ReplacementText = (" " + OverrideSpelling).str();
  148. auto *LastTokenIter = std::prev(Tokens.end());
  149. // When try statement is used instead of compound statement as
  150. // method body - insert override keyword before it.
  151. if (LastTokenIter->is(tok::kw_try))
  152. LastTokenIter = std::prev(LastTokenIter);
  153. InsertLoc = LastTokenIter->getEndLoc();
  154. }
  155. if (!InsertLoc.isValid()) {
  156. // For declarations marked with "= 0" or "= [default|delete]", the end
  157. // location will point until after those markings. Therefore, the override
  158. // keyword shouldn't be inserted at the end, but before the '='.
  159. if (Tokens.size() > 2 &&
  160. (getText(Tokens.back(), Sources) == "0" ||
  161. Tokens.back().is(tok::kw_default) ||
  162. Tokens.back().is(tok::kw_delete)) &&
  163. getText(Tokens[Tokens.size() - 2], Sources) == "=") {
  164. InsertLoc = Tokens[Tokens.size() - 2].getLocation();
  165. // Check if we need to insert a space.
  166. if ((Tokens[Tokens.size() - 2].getFlags() & Token::LeadingSpace) == 0)
  167. ReplacementText = (" " + OverrideSpelling + " ").str();
  168. } else if (getText(Tokens.back(), Sources) == "ABSTRACT")
  169. InsertLoc = Tokens.back().getLocation();
  170. }
  171. if (!InsertLoc.isValid()) {
  172. InsertLoc = FileRange.getEnd();
  173. ReplacementText = (" " + OverrideSpelling).str();
  174. }
  175. // If the override macro has been specified just ensure it exists,
  176. // if not don't apply a fixit but keep the warning.
  177. if (OverrideSpelling != "override" &&
  178. !Context.Idents.get(OverrideSpelling).hasMacroDefinition())
  179. return;
  180. Diag << FixItHint::CreateInsertion(InsertLoc, ReplacementText);
  181. }
  182. if (HasFinal && HasOverride && !AllowOverrideAndFinal) {
  183. SourceLocation OverrideLoc = Method->getAttr<OverrideAttr>()->getLocation();
  184. Diag << FixItHint::CreateRemoval(
  185. CharSourceRange::getTokenRange(OverrideLoc, OverrideLoc));
  186. }
  187. if (HasVirtual) {
  188. for (Token Tok : Tokens) {
  189. if (Tok.is(tok::kw_virtual)) {
  190. Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
  191. Tok.getLocation(), Tok.getLocation()));
  192. break;
  193. }
  194. }
  195. }
  196. }
  197. } // namespace clang::tidy::modernize