1 //===--- ReplaceDisallowCopyAndAssignMacroCheck.cpp - clang-tidy ----------===// 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 "ReplaceDisallowCopyAndAssignMacroCheck.h" 10 #include "clang/Frontend/CompilerInstance.h" 11 #include "clang/Lex/MacroArgs.h" 12 #include "llvm/Support/FormatVariadic.h" 13 14 namespace clang { 15 namespace tidy { 16 namespace modernize { 17 18 namespace { 19 20 class ReplaceDisallowCopyAndAssignMacroCallbacks : public PPCallbacks { 21 public: 22 explicit ReplaceDisallowCopyAndAssignMacroCallbacks( 23 ReplaceDisallowCopyAndAssignMacroCheck &Check, Preprocessor &PP) 24 : Check(Check), PP(PP) {} 25 26 void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD, 27 SourceRange Range, const MacroArgs *Args) override { 28 IdentifierInfo *Info = MacroNameTok.getIdentifierInfo(); 29 if (!Info || !Args || Args->getNumMacroArguments() != 1) 30 return; 31 if (Info->getName() != Check.getMacroName()) 32 return; 33 // The first argument to the DISALLOW_COPY_AND_ASSIGN macro is exptected to 34 // be the class name. 35 const Token *ClassNameTok = Args->getUnexpArgument(0); 36 if (Args->ArgNeedsPreexpansion(ClassNameTok, PP)) 37 // For now we only support simple argument that don't need to be 38 // pre-expanded. 39 return; 40 clang::IdentifierInfo *ClassIdent = ClassNameTok->getIdentifierInfo(); 41 if (!ClassIdent) 42 return; 43 44 std::string Replacement = llvm::formatv( 45 R"cpp({0}(const {0} &) = delete; 46 const {0} &operator=(const {0} &) = delete{1})cpp", 47 ClassIdent->getName(), shouldAppendSemi(Range) ? ";" : ""); 48 49 Check.diag(MacroNameTok.getLocation(), 50 "prefer deleting copy constructor and assignment operator over " 51 "using macro '%0'") 52 << Check.getMacroName() 53 << FixItHint::CreateReplacement( 54 PP.getSourceManager().getExpansionRange(Range), Replacement); 55 } 56 57 private: 58 /// \returns \c true if the next token after the given \p MacroLoc is \b not a 59 /// semicolon. 60 bool shouldAppendSemi(SourceRange MacroLoc) { 61 llvm::Optional<Token> Next = Lexer::findNextToken( 62 MacroLoc.getEnd(), PP.getSourceManager(), PP.getLangOpts()); 63 return !(Next && Next->is(tok::semi)); 64 } 65 66 ReplaceDisallowCopyAndAssignMacroCheck &Check; 67 Preprocessor &PP; 68 }; 69 } // namespace 70 71 ReplaceDisallowCopyAndAssignMacroCheck::ReplaceDisallowCopyAndAssignMacroCheck( 72 StringRef Name, ClangTidyContext *Context) 73 : ClangTidyCheck(Name, Context), 74 MacroName(Options.get("MacroName", "DISALLOW_COPY_AND_ASSIGN")) {} 75 76 void ReplaceDisallowCopyAndAssignMacroCheck::registerPPCallbacks( 77 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { 78 PP->addPPCallbacks( 79 ::std::make_unique<ReplaceDisallowCopyAndAssignMacroCallbacks>( 80 *this, *ModuleExpanderPP)); 81 } 82 83 void ReplaceDisallowCopyAndAssignMacroCheck::storeOptions( 84 ClangTidyOptions::OptionMap &Opts) { 85 Options.store(Opts, "MacroName", MacroName); 86 } 87 88 } // namespace modernize 89 } // namespace tidy 90 } // namespace clang 91