clang-tools 22.0.0git
UseOverrideCheck.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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 "../utils/LexerUtils.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Lex/Lexer.h"
14
15using namespace clang::ast_matchers;
16
17namespace clang::tidy::modernize {
18
20 : ClangTidyCheck(Name, Context),
21 IgnoreDestructors(Options.get("IgnoreDestructors", false)),
22 IgnoreTemplateInstantiations(
23 Options.get("IgnoreTemplateInstantiations", false)),
24 AllowOverrideAndFinal(Options.get("AllowOverrideAndFinal", false)),
25 OverrideSpelling(Options.get("OverrideSpelling", "override")),
26 FinalSpelling(Options.get("FinalSpelling", "final")) {}
27
29 Options.store(Opts, "IgnoreDestructors", IgnoreDestructors);
30 Options.store(Opts, "IgnoreTemplateInstantiations",
31 IgnoreTemplateInstantiations);
32 Options.store(Opts, "AllowOverrideAndFinal", AllowOverrideAndFinal);
33 Options.store(Opts, "OverrideSpelling", OverrideSpelling);
34 Options.store(Opts, "FinalSpelling", FinalSpelling);
35}
36
37void 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);
50}
51
52// Re-lex the tokens to get precise locations to insert 'override' and remove
53// 'virtual'.
54static SmallVector<Token, 16>
55parseTokens(CharSourceRange Range, const MatchFinder::MatchResult &Result) {
56 const SourceManager &Sources = *Result.SourceManager;
57 const std::pair<FileID, unsigned> LocInfo =
58 Sources.getDecomposedLoc(Range.getBegin());
59 const 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());
81 }
82 Tokens.push_back(Tok);
83 }
84 return Tokens;
85}
86
87void 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 const bool HasVirtual = Method->isVirtualAsWritten();
102 const bool HasOverride = Method->getAttr<OverrideAttr>();
103 const bool HasFinal = Method->getAttr<FinalAttr>();
104
105 const bool OnlyVirtualSpecified = HasVirtual && !HasOverride && !HasFinal;
106 const 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 const StringRef Redundant =
119 HasVirtual ? (HasOverride && HasFinal && !AllowOverrideAndFinal
120 ? "'virtual' and '%0' are"
121 : "'virtual' is")
122 : "'%0' is";
123 const 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 const 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 the 'virtual' token,
141 // store the location of 'virtual' in each FunctionDecl.
142 SmallVector<Token, 16> Tokens = parseTokens(FileRange, Result);
143
144 // Add 'override' on inline declarations that don't already have it.
145 if (!HasFinal && !HasOverride) {
146 // If the override macro has been specified just ensure it exists,
147 // if not don't apply a fixit but keep the warning.
148 if (OverrideSpelling != "override" &&
149 !Context.Idents.get(OverrideSpelling).hasMacroDefinition())
150 return;
151
152 Diag << FixItHint::CreateInsertion(
153 Lexer::getLocForEndOfToken(
154 Method->getTypeSourceInfo()->getTypeLoc().getEndLoc(), 0, Sources,
155 getLangOpts()),
156 (" " + OverrideSpelling).str());
157 }
158
159 if (HasFinal && HasOverride && !AllowOverrideAndFinal)
160 Diag << FixItHint::CreateRemoval(
161 Method->getAttr<OverrideAttr>()->getLocation());
162
163 if (HasVirtual) {
164 for (const Token Tok : Tokens) {
165 if (Tok.is(tok::kw_virtual)) {
166 std::optional<Token> NextToken =
168 Tok.getEndLoc(), Sources, getLangOpts());
169 if (NextToken.has_value()) {
170 Diag << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
171 Tok.getLocation(), NextToken->getLocation()));
172 break;
173 }
174 }
175 }
176 }
177}
178
179} // namespace clang::tidy::modernize
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
UseOverrideCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
static SmallVector< Token, 16 > parseTokens(CharSourceRange Range, const MatchFinder::MatchResult &Result)
std::optional< Token > findNextTokenIncludingComments(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts)
Definition LexerUtils.h:93
llvm::StringMap< ClangTidyValue > OptionMap