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