clang-tools 23.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 AllowVirtualAndOverride(Options.get("AllowVirtualAndOverride", false)),
26 OverrideSpelling(Options.get("OverrideSpelling", "override")),
27 FinalSpelling(Options.get("FinalSpelling", "final")) {}
28
30 Options.store(Opts, "IgnoreDestructors", IgnoreDestructors);
31 Options.store(Opts, "IgnoreTemplateInstantiations",
32 IgnoreTemplateInstantiations);
33 Options.store(Opts, "AllowOverrideAndFinal", AllowOverrideAndFinal);
34 Options.store(Opts, "AllowVirtualAndOverride", AllowVirtualAndOverride);
35 Options.store(Opts, "OverrideSpelling", OverrideSpelling);
36 Options.store(Opts, "FinalSpelling", FinalSpelling);
37}
38
39void UseOverrideCheck::registerMatchers(MatchFinder *Finder) {
40 auto IgnoreDestructorMatcher =
41 IgnoreDestructors ? cxxMethodDecl(unless(cxxDestructorDecl()))
42 : cxxMethodDecl();
43 auto IgnoreTemplateInstantiationsMatcher =
44 IgnoreTemplateInstantiations
45 ? cxxMethodDecl(unless(ast_matchers::isTemplateInstantiation()))
46 : cxxMethodDecl();
47 Finder->addMatcher(cxxMethodDecl(isOverride(),
48 IgnoreTemplateInstantiationsMatcher,
49 IgnoreDestructorMatcher)
50 .bind("method"),
51 this);
52}
53
54// Re-lex the tokens to get precise locations to insert 'override' and remove
55// 'virtual'.
57parseTokens(CharSourceRange Range, const MatchFinder::MatchResult &Result) {
58 const SourceManager &Sources = *Result.SourceManager;
59 const std::pair<FileID, unsigned> LocInfo =
60 Sources.getDecomposedLoc(Range.getBegin());
61 const StringRef File = Sources.getBufferData(LocInfo.first);
62 const char *TokenBegin = File.data() + LocInfo.second;
63 Lexer RawLexer(Sources.getLocForStartOfFile(LocInfo.first),
64 Result.Context->getLangOpts(), File.begin(), TokenBegin,
65 File.end());
67 Token Tok;
68 int NestedParens = 0;
69 while (!RawLexer.LexFromRawLexer(Tok)) {
70 if ((Tok.is(tok::semi) || Tok.is(tok::l_brace)) && NestedParens == 0)
71 break;
72 if (Sources.isBeforeInTranslationUnit(Range.getEnd(), Tok.getLocation()))
73 break;
74 if (Tok.is(tok::l_paren))
75 ++NestedParens;
76 else if (Tok.is(tok::r_paren))
77 --NestedParens;
78 if (Tok.is(tok::raw_identifier)) {
79 IdentifierInfo &Info = Result.Context->Idents.get(StringRef(
80 Sources.getCharacterData(Tok.getLocation()), Tok.getLength()));
81 Tok.setIdentifierInfo(&Info);
82 Tok.setKind(Info.getTokenID());
83 }
84 Tokens.push_back(Tok);
85 }
86 return Tokens;
87}
88
89void UseOverrideCheck::check(const MatchFinder::MatchResult &Result) {
90 const auto *Method = Result.Nodes.getNodeAs<FunctionDecl>("method");
91 const SourceManager &Sources = *Result.SourceManager;
92
93 ASTContext &Context = *Result.Context;
94
95 assert(Method != nullptr);
96 if (Method->getInstantiatedFromMemberFunction() != nullptr)
97 Method = Method->getInstantiatedFromMemberFunction();
98
99 if (Method->isImplicit() || Method->getLocation().isMacroID() ||
100 Method->isOutOfLine())
101 return;
102
103 const bool HasVirtual = Method->isVirtualAsWritten();
104 const bool HasOverride = Method->getAttr<OverrideAttr>();
105 const bool HasFinal = Method->getAttr<FinalAttr>();
106
107 const bool OnlyVirtualSpecified = HasVirtual && !HasOverride && !HasFinal;
108 const unsigned KeywordCount = HasVirtual + HasOverride + HasFinal;
109
110 const bool AcceptsOverrideAndFinal =
111 !HasVirtual && HasOverride && HasFinal && AllowOverrideAndFinal;
112 const bool AcceptsVirtualAndOverride = HasVirtual && HasOverride &&
113 AllowVirtualAndOverride &&
114 (!HasFinal || AllowOverrideAndFinal);
115
116 if ((!OnlyVirtualSpecified && KeywordCount == 1) || AcceptsOverrideAndFinal ||
117 AcceptsVirtualAndOverride)
118 return; // Nothing to do.
119
120 std::string Message;
121 if (OnlyVirtualSpecified) {
122 Message = "prefer using '%0' or (rarely) '%1' instead of 'virtual'";
123 } else if (KeywordCount == 0) {
124 Message = "annotate this function with '%0' or (rarely) '%1'";
125 } else {
126 const StringRef Redundant =
127 HasVirtual ? (HasOverride && HasFinal && !AllowOverrideAndFinal
128 ? "'virtual' and '%0' are"
129 : "'virtual' is")
130 : "'%0' is";
131 const StringRef Correct = HasFinal ? "'%1'" : "'%0'";
132
133 Message = (llvm::Twine(Redundant) +
134 " redundant since the function is already declared " + Correct)
135 .str();
136 }
137
138 auto Diag = diag(Method->getLocation(), Message)
139 << OverrideSpelling << FinalSpelling;
140
141 const CharSourceRange FileRange = Lexer::makeFileCharRange(
142 CharSourceRange::getTokenRange(Method->getSourceRange()), Sources,
143 getLangOpts());
144
145 if (!FileRange.isValid())
146 return;
147
148 // FIXME: Instead of re-lexing and looking for the 'virtual' token,
149 // store the location of 'virtual' in each FunctionDecl.
150 const SmallVector<Token, 16> Tokens = parseTokens(FileRange, Result);
151
152 // Add 'override' on inline declarations that don't already have it.
153 if (!HasFinal && !HasOverride) {
154 // If the override macro has been specified just ensure it exists,
155 // if not don't apply a fixit but keep the warning.
156 if (OverrideSpelling != "override" &&
157 !Context.Idents.get(OverrideSpelling).hasMacroDefinition())
158 return;
159
160 Diag << FixItHint::CreateInsertion(
161 Lexer::getLocForEndOfToken(
162 Method->getTypeSourceInfo()->getTypeLoc().getEndLoc(), 0, Sources,
163 getLangOpts()),
164 (" " + OverrideSpelling).str());
165 }
166
167 if (HasFinal && HasOverride && !AllowOverrideAndFinal)
168 Diag << FixItHint::CreateRemoval(
169 Method->getAttr<OverrideAttr>()->getLocation());
170
171 if (HasVirtual) {
172 for (const Token Tok : Tokens) {
173 if (Tok.is(tok::kw_virtual)) {
174 std::optional<Token> NextToken =
176 Tok.getEndLoc(), Sources, getLangOpts());
177 if (NextToken.has_value()) {
178 Diag << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
179 Tok.getLocation(), NextToken->getLocation()));
180 break;
181 }
182 }
183 }
184 }
185}
186
187} // 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)
static constexpr StringRef Message
std::optional< Token > findNextTokenIncludingComments(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts)
Definition LexerUtils.h:99
llvm::StringMap< ClangTidyValue > OptionMap