| //===--- ReplaceDisallowCopyAndAssignMacroCheck.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 "ReplaceDisallowCopyAndAssignMacroCheck.h" |
| #include "clang/Frontend/CompilerInstance.h" |
| #include "clang/Lex/MacroArgs.h" |
| #include "clang/Lex/PPCallbacks.h" |
| #include "clang/Lex/Preprocessor.h" |
| #include "llvm/Support/FormatVariadic.h" |
| #include <optional> |
| |
| namespace clang::tidy::modernize { |
| |
| namespace { |
| |
| class ReplaceDisallowCopyAndAssignMacroCallbacks : public PPCallbacks { |
| public: |
| explicit ReplaceDisallowCopyAndAssignMacroCallbacks( |
| ReplaceDisallowCopyAndAssignMacroCheck &Check, Preprocessor &PP) |
| : Check(Check), PP(PP) {} |
| |
| void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD, |
| SourceRange Range, const MacroArgs *Args) override { |
| IdentifierInfo *Info = MacroNameTok.getIdentifierInfo(); |
| if (!Info || !Args || Args->getNumMacroArguments() != 1) |
| return; |
| if (Info->getName() != Check.getMacroName()) |
| return; |
| // The first argument to the DISALLOW_COPY_AND_ASSIGN macro is expected to |
| // be the class name. |
| const Token *ClassNameTok = Args->getUnexpArgument(0); |
| if (Args->ArgNeedsPreexpansion(ClassNameTok, PP)) |
| // For now we only support simple argument that don't need to be |
| // pre-expanded. |
| return; |
| clang::IdentifierInfo *ClassIdent = ClassNameTok->getIdentifierInfo(); |
| if (!ClassIdent) |
| return; |
| |
| std::string Replacement = llvm::formatv( |
| R"cpp({0}(const {0} &) = delete; |
| const {0} &operator=(const {0} &) = delete{1})cpp", |
| ClassIdent->getName(), shouldAppendSemi(Range) ? ";" : ""); |
| |
| Check.diag(MacroNameTok.getLocation(), |
| "prefer deleting copy constructor and assignment operator over " |
| "using macro '%0'") |
| << Check.getMacroName() |
| << FixItHint::CreateReplacement( |
| PP.getSourceManager().getExpansionRange(Range), Replacement); |
| } |
| |
| private: |
| /// \returns \c true if the next token after the given \p MacroLoc is \b not a |
| /// semicolon. |
| bool shouldAppendSemi(SourceRange MacroLoc) { |
| std::optional<Token> Next = Lexer::findNextToken( |
| MacroLoc.getEnd(), PP.getSourceManager(), PP.getLangOpts()); |
| return !(Next && Next->is(tok::semi)); |
| } |
| |
| ReplaceDisallowCopyAndAssignMacroCheck &Check; |
| Preprocessor &PP; |
| }; |
| } // namespace |
| |
| ReplaceDisallowCopyAndAssignMacroCheck::ReplaceDisallowCopyAndAssignMacroCheck( |
| StringRef Name, ClangTidyContext *Context) |
| : ClangTidyCheck(Name, Context), |
| MacroName(Options.get("MacroName", "DISALLOW_COPY_AND_ASSIGN")) {} |
| |
| void ReplaceDisallowCopyAndAssignMacroCheck::registerPPCallbacks( |
| const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { |
| PP->addPPCallbacks( |
| ::std::make_unique<ReplaceDisallowCopyAndAssignMacroCallbacks>( |
| *this, *ModuleExpanderPP)); |
| } |
| |
| void ReplaceDisallowCopyAndAssignMacroCheck::storeOptions( |
| ClangTidyOptions::OptionMap &Opts) { |
| Options.store(Opts, "MacroName", MacroName); |
| } |
| |
| } // namespace clang::tidy::modernize |