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,
64 SmallVector<Token, 16> Tokens;
67 while (!RawLexer.LexFromRawLexer(Tok)) {
68 if ((Tok.is(tok::semi) || Tok.is(tok::l_brace)) && NestedParens == 0)
70 if (Sources.isBeforeInTranslationUnit(Range.getEnd(), Tok.getLocation()))
72 if (Tok.is(tok::l_paren))
74 else if (Tok.is(tok::r_paren))
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);
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())
105 const bool HasVirtual = Method->isVirtualAsWritten();
106 const bool HasOverride = Method->getAttr<OverrideAttr>();
107 const bool HasFinal = Method->getAttr<FinalAttr>();
109 const bool OnlyVirtualSpecified = HasVirtual && !HasOverride && !HasFinal;
110 const unsigned KeywordCount = HasVirtual + HasOverride + HasFinal;
112 if ((!OnlyVirtualSpecified && KeywordCount == 1) ||
113 (!HasVirtual && HasOverride && HasFinal && AllowOverrideAndFinal))
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'";
122 const StringRef Redundant =
123 HasVirtual ? (HasOverride && HasFinal && !AllowOverrideAndFinal
124 ?
"'virtual' and '%0' are"
127 const StringRef Correct = HasFinal ?
"'%1'" :
"'%0'";
129 Message = (llvm::Twine(Redundant) +
130 " redundant since the function is already declared " + Correct)
134 auto Diag = diag(Method->getLocation(),
Message)
135 << OverrideSpelling << FinalSpelling;
137 const CharSourceRange FileRange = Lexer::makeFileCharRange(
138 CharSourceRange::getTokenRange(Method->getSourceRange()), Sources,
141 if (!FileRange.isValid())
147 SmallVector<Token, 16> Tokens =
parseTokens(FileRange, Result);
150 if (!HasFinal && !HasOverride) {
151 SourceLocation InsertLoc;
152 std::string ReplacementText = (OverrideSpelling +
" ").str();
153 const SourceLocation MethodLoc = Method->getLocation();
155 for (
const Token T : Tokens) {
156 if (T.is(tok::kw___attribute) &&
157 !Sources.isBeforeInTranslationUnit(T.getLocation(), MethodLoc)) {
158 InsertLoc = T.getLocation();
163 if (Method->hasAttrs()) {
164 for (
const clang::Attr *A : Method->getAttrs()) {
165 if (!A->isImplicit() && !A->isInherited()) {
166 const SourceLocation Loc =
167 Sources.getExpansionLoc(A->getRange().getBegin());
168 if ((!InsertLoc.isValid() ||
169 Sources.isBeforeInTranslationUnit(Loc, InsertLoc)) &&
170 !Sources.isBeforeInTranslationUnit(Loc, MethodLoc))
176 if (InsertLoc.isInvalid() && Method->doesThisDeclarationHaveABody() &&
177 Method->getBody() && !Method->isDefaulted()) {
182 ReplacementText = (
" " + OverrideSpelling).str();
183 auto *LastTokenIter = std::prev(Tokens.end());
186 if (LastTokenIter->is(tok::kw_try))
187 LastTokenIter = std::prev(LastTokenIter);
188 InsertLoc = LastTokenIter->getEndLoc();
191 if (!InsertLoc.isValid()) {
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();
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();
215 if (OverrideSpelling !=
"override" &&
216 !Context.Idents.get(OverrideSpelling).hasMacroDefinition())
219 Diag << FixItHint::CreateInsertion(InsertLoc, ReplacementText);
222 if (HasFinal && HasOverride && !AllowOverrideAndFinal) {
223 const SourceLocation OverrideLoc =
224 Method->getAttr<OverrideAttr>()->getLocation();
225 Diag << FixItHint::CreateRemoval(
226 CharSourceRange::getTokenRange(OverrideLoc, OverrideLoc));
230 for (
const Token Tok : Tokens) {
231 if (Tok.is(tok::kw_virtual)) {
232 std::optional<Token> NextToken =
234 Tok.getEndLoc(), Sources, getLangOpts());
235 if (NextToken.has_value()) {
236 Diag << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
237 Tok.getLocation(), NextToken->getLocation()));
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.