123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- //===--- UseOverrideCheck.cpp - clang-tidy --------------------------------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #include "UseOverrideCheck.h"
- #include "clang/AST/ASTContext.h"
- #include "clang/ASTMatchers/ASTMatchFinder.h"
- #include "clang/Lex/Lexer.h"
- using namespace clang::ast_matchers;
- namespace clang::tidy::modernize {
- UseOverrideCheck::UseOverrideCheck(StringRef Name, ClangTidyContext *Context)
- : ClangTidyCheck(Name, Context),
- IgnoreDestructors(Options.get("IgnoreDestructors", false)),
- AllowOverrideAndFinal(Options.get("AllowOverrideAndFinal", false)),
- OverrideSpelling(Options.get("OverrideSpelling", "override")),
- FinalSpelling(Options.get("FinalSpelling", "final")) {}
- void UseOverrideCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
- Options.store(Opts, "IgnoreDestructors", IgnoreDestructors);
- Options.store(Opts, "AllowOverrideAndFinal", AllowOverrideAndFinal);
- Options.store(Opts, "OverrideSpelling", OverrideSpelling);
- Options.store(Opts, "FinalSpelling", FinalSpelling);
- }
- void UseOverrideCheck::registerMatchers(MatchFinder *Finder) {
- if (IgnoreDestructors)
- Finder->addMatcher(
- cxxMethodDecl(isOverride(), unless(cxxDestructorDecl())).bind("method"),
- this);
- else
- Finder->addMatcher(cxxMethodDecl(isOverride()).bind("method"), this);
- }
- // Re-lex the tokens to get precise locations to insert 'override' and remove
- // 'virtual'.
- static SmallVector<Token, 16>
- parseTokens(CharSourceRange Range, const MatchFinder::MatchResult &Result) {
- const SourceManager &Sources = *Result.SourceManager;
- std::pair<FileID, unsigned> LocInfo =
- Sources.getDecomposedLoc(Range.getBegin());
- StringRef File = Sources.getBufferData(LocInfo.first);
- const char *TokenBegin = File.data() + LocInfo.second;
- Lexer RawLexer(Sources.getLocForStartOfFile(LocInfo.first),
- Result.Context->getLangOpts(), File.begin(), TokenBegin,
- File.end());
- SmallVector<Token, 16> Tokens;
- Token Tok;
- int NestedParens = 0;
- while (!RawLexer.LexFromRawLexer(Tok)) {
- if ((Tok.is(tok::semi) || Tok.is(tok::l_brace)) && NestedParens == 0)
- break;
- if (Sources.isBeforeInTranslationUnit(Range.getEnd(), Tok.getLocation()))
- break;
- if (Tok.is(tok::l_paren))
- ++NestedParens;
- else if (Tok.is(tok::r_paren))
- --NestedParens;
- if (Tok.is(tok::raw_identifier)) {
- IdentifierInfo &Info = Result.Context->Idents.get(StringRef(
- Sources.getCharacterData(Tok.getLocation()), Tok.getLength()));
- Tok.setIdentifierInfo(&Info);
- Tok.setKind(Info.getTokenID());
- }
- Tokens.push_back(Tok);
- }
- return Tokens;
- }
- static StringRef getText(const Token &Tok, const SourceManager &Sources) {
- return StringRef(Sources.getCharacterData(Tok.getLocation()),
- Tok.getLength());
- }
- void UseOverrideCheck::check(const MatchFinder::MatchResult &Result) {
- const auto *Method = Result.Nodes.getNodeAs<FunctionDecl>("method");
- const SourceManager &Sources = *Result.SourceManager;
- ASTContext &Context = *Result.Context;
- assert(Method != nullptr);
- if (Method->getInstantiatedFromMemberFunction() != nullptr)
- Method = Method->getInstantiatedFromMemberFunction();
- if (Method->isImplicit() || Method->getLocation().isMacroID() ||
- Method->isOutOfLine())
- return;
- bool HasVirtual = Method->isVirtualAsWritten();
- bool HasOverride = Method->getAttr<OverrideAttr>();
- bool HasFinal = Method->getAttr<FinalAttr>();
- bool OnlyVirtualSpecified = HasVirtual && !HasOverride && !HasFinal;
- unsigned KeywordCount = HasVirtual + HasOverride + HasFinal;
- if ((!OnlyVirtualSpecified && KeywordCount == 1) ||
- (!HasVirtual && HasOverride && HasFinal && AllowOverrideAndFinal))
- return; // Nothing to do.
- std::string Message;
- if (OnlyVirtualSpecified) {
- Message = "prefer using '%0' or (rarely) '%1' instead of 'virtual'";
- } else if (KeywordCount == 0) {
- Message = "annotate this function with '%0' or (rarely) '%1'";
- } else {
- StringRef Redundant =
- HasVirtual ? (HasOverride && HasFinal && !AllowOverrideAndFinal
- ? "'virtual' and '%0' are"
- : "'virtual' is")
- : "'%0' is";
- StringRef Correct = HasFinal ? "'%1'" : "'%0'";
- Message = (llvm::Twine(Redundant) +
- " redundant since the function is already declared " + Correct)
- .str();
- }
- auto Diag = diag(Method->getLocation(), Message)
- << OverrideSpelling << FinalSpelling;
- CharSourceRange FileRange = Lexer::makeFileCharRange(
- CharSourceRange::getTokenRange(Method->getSourceRange()), Sources,
- getLangOpts());
- if (!FileRange.isValid())
- return;
- // FIXME: Instead of re-lexing and looking for specific macros such as
- // 'ABSTRACT', properly store the location of 'virtual' and '= 0' in each
- // FunctionDecl.
- SmallVector<Token, 16> Tokens = parseTokens(FileRange, Result);
- // Add 'override' on inline declarations that don't already have it.
- if (!HasFinal && !HasOverride) {
- SourceLocation InsertLoc;
- std::string ReplacementText = (OverrideSpelling + " ").str();
- SourceLocation MethodLoc = Method->getLocation();
- for (Token T : Tokens) {
- if (T.is(tok::kw___attribute) &&
- !Sources.isBeforeInTranslationUnit(T.getLocation(), MethodLoc)) {
- InsertLoc = T.getLocation();
- break;
- }
- }
- if (Method->hasAttrs()) {
- for (const clang::Attr *A : Method->getAttrs()) {
- if (!A->isImplicit() && !A->isInherited()) {
- SourceLocation Loc =
- Sources.getExpansionLoc(A->getRange().getBegin());
- if ((!InsertLoc.isValid() ||
- Sources.isBeforeInTranslationUnit(Loc, InsertLoc)) &&
- !Sources.isBeforeInTranslationUnit(Loc, MethodLoc))
- InsertLoc = Loc;
- }
- }
- }
- if (InsertLoc.isInvalid() && Method->doesThisDeclarationHaveABody() &&
- Method->getBody() && !Method->isDefaulted()) {
- // For methods with inline definition, add the override keyword at the
- // end of the declaration of the function, but prefer to put it on the
- // same line as the declaration if the beginning brace for the start of
- // the body falls on the next line.
- ReplacementText = (" " + OverrideSpelling).str();
- auto *LastTokenIter = std::prev(Tokens.end());
- // When try statement is used instead of compound statement as
- // method body - insert override keyword before it.
- if (LastTokenIter->is(tok::kw_try))
- LastTokenIter = std::prev(LastTokenIter);
- InsertLoc = LastTokenIter->getEndLoc();
- }
- if (!InsertLoc.isValid()) {
- // For declarations marked with "= 0" or "= [default|delete]", the end
- // location will point until after those markings. Therefore, the override
- // keyword shouldn't be inserted at the end, but before the '='.
- if (Tokens.size() > 2 &&
- (getText(Tokens.back(), Sources) == "0" ||
- Tokens.back().is(tok::kw_default) ||
- Tokens.back().is(tok::kw_delete)) &&
- getText(Tokens[Tokens.size() - 2], Sources) == "=") {
- InsertLoc = Tokens[Tokens.size() - 2].getLocation();
- // Check if we need to insert a space.
- if ((Tokens[Tokens.size() - 2].getFlags() & Token::LeadingSpace) == 0)
- ReplacementText = (" " + OverrideSpelling + " ").str();
- } else if (getText(Tokens.back(), Sources) == "ABSTRACT")
- InsertLoc = Tokens.back().getLocation();
- }
- if (!InsertLoc.isValid()) {
- InsertLoc = FileRange.getEnd();
- ReplacementText = (" " + OverrideSpelling).str();
- }
- // If the override macro has been specified just ensure it exists,
- // if not don't apply a fixit but keep the warning.
- if (OverrideSpelling != "override" &&
- !Context.Idents.get(OverrideSpelling).hasMacroDefinition())
- return;
- Diag << FixItHint::CreateInsertion(InsertLoc, ReplacementText);
- }
- if (HasFinal && HasOverride && !AllowOverrideAndFinal) {
- SourceLocation OverrideLoc = Method->getAttr<OverrideAttr>()->getLocation();
- Diag << FixItHint::CreateRemoval(
- CharSourceRange::getTokenRange(OverrideLoc, OverrideLoc));
- }
- if (HasVirtual) {
- for (Token Tok : Tokens) {
- if (Tok.is(tok::kw_virtual)) {
- Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
- Tok.getLocation(), Tok.getLocation()));
- break;
- }
- }
- }
- }
- } // namespace clang::tidy::modernize
|