10#include "clang/Basic/Builtins.h"
11#include "clang/Frontend/CompilerInstance.h"
12#include "clang/Lex/MacroArgs.h"
13#include "clang/Lex/PPCallbacks.h"
14#include "clang/Lex/Preprocessor.h"
20class MacroRepeatedPPCallbacks :
public PPCallbacks {
22 MacroRepeatedPPCallbacks(ClangTidyCheck &Check, Preprocessor &PP)
23 : Check(Check), PP(PP) {}
25 void MacroExpands(
const Token &MacroNameTok,
const MacroDefinition &MD,
26 SourceRange
Range,
const MacroArgs *
Args)
override;
29 ClangTidyCheck &Check;
32 unsigned countArgumentExpansions(
const MacroInfo *MI,
33 const IdentifierInfo *Arg)
const;
35 bool hasSideEffects(
const Token *ResultArgToks)
const;
39void MacroRepeatedPPCallbacks::MacroExpands(
const Token &MacroNameTok,
40 const MacroDefinition &MD,
42 const MacroArgs *
Args) {
44 if (!
Range.getBegin().isFileID())
47 const MacroInfo *MI = MD.getMacroInfo();
51 if (llvm::any_of(MI->tokens(), [](
const Token &T) {
52 return T.isOneOf(tok::kw_if, tok::kw_else, tok::kw_switch, tok::kw_case,
53 tok::kw_break, tok::kw_while, tok::kw_do, tok::kw_for,
54 tok::kw_continue, tok::kw_goto, tok::kw_return);
58 for (
unsigned ArgNo = 0U; ArgNo < MI->getNumParams(); ++ArgNo) {
59 const IdentifierInfo *Arg = *(MI->param_begin() + ArgNo);
60 const Token *ResultArgToks =
Args->getUnexpArgument(ArgNo);
62 if (hasSideEffects(ResultArgToks) &&
63 countArgumentExpansions(MI, Arg) >= 2) {
64 Check.diag(ResultArgToks->getLocation(),
65 "side effects in the %ordinal0 macro argument %1 are "
66 "repeated in macro expansion")
67 << (ArgNo + 1) << Arg;
68 Check.diag(MI->getDefinitionLoc(),
"macro %0 defined here",
70 << MacroNameTok.getIdentifierInfo();
75unsigned MacroRepeatedPPCallbacks::countArgumentExpansions(
76 const MacroInfo *MI,
const IdentifierInfo *Arg)
const {
82 bool SkipParen =
false;
83 int SkipParenCount = 0;
85 bool FoundBuiltin =
false;
86 bool PrevTokenIsHash =
false;
89 std::stack<unsigned, SmallVector<unsigned, 8>> CountAtQuestion;
90 for (
const auto &T : MI->tokens()) {
96 if (FoundBuiltin &&
T.isOneOf(tok::question, tok::ampamp, tok::pipepipe))
100 if (
T.is(tok::hash)) {
101 PrevTokenIsHash =
true;
104 if (PrevTokenIsHash) {
105 PrevTokenIsHash =
false;
110 if (
T.is(tok::question)) {
111 CountAtQuestion.push(Current);
112 }
else if (
T.is(tok::colon)) {
113 if (CountAtQuestion.empty())
115 Current = CountAtQuestion.top();
116 CountAtQuestion.pop();
121 if (
T.is(tok::l_paren))
123 else if (
T.is(tok::r_paren))
125 SkipParen = (SkipParenCount != 0);
130 IdentifierInfo *TII =
T.getIdentifierInfo();
138 if (TII->getBuiltinID() == Builtin::BI__builtin_constant_p) {
146 if (TII->hasMacroDefinition()) {
147 const MacroInfo *
M =
PP.getMacroDefinition(TII).getMacroInfo();
148 if (
M !=
nullptr &&
M->isFunctionLike())
163bool MacroRepeatedPPCallbacks::hasSideEffects(
164 const Token *ResultArgToks)
const {
165 for (; ResultArgToks->isNot(tok::eof); ++ResultArgToks) {
166 if (ResultArgToks->isOneOf(tok::plusplus, tok::minusminus))
173 const SourceManager &SM, Preprocessor *
PP, Preprocessor *ModuleExpanderPP) {
174 PP->addPPCallbacks(::std::make_unique<MacroRepeatedPPCallbacks>(*
this, *
PP));
CharSourceRange Range
SourceRange for the file name.
const google::protobuf::Message & M
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
Override this to register PPCallbacks in the preprocessor.