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