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