clang-tools  14.0.0git
MacroRepeatedSideEffectsCheck.cpp
Go to the documentation of this file.
1 //===--- MacroRepeatedSideEffectsCheck.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 
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"
15 
16 namespace clang {
17 namespace tidy {
18 namespace bugprone {
19 
20 namespace {
21 class MacroRepeatedPPCallbacks : public PPCallbacks {
22 public:
23  MacroRepeatedPPCallbacks(ClangTidyCheck &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 
29 private:
30  ClangTidyCheck &Check;
31  Preprocessor &PP;
32 
33  unsigned countArgumentExpansions(const MacroInfo *MI,
34  const IdentifierInfo *Arg) const;
35 
36  bool hasSideEffects(const Token *ResultArgToks) const;
37 };
38 } // End of anonymous namespace.
39 
40 void MacroRepeatedPPCallbacks::MacroExpands(const Token &MacroNameTok,
41  const MacroDefinition &MD,
42  SourceRange Range,
43  const MacroArgs *Args) {
44  // Ignore macro argument expansions.
45  if (!Range.getBegin().isFileID())
46  return;
47 
48  const MacroInfo *MI = MD.getMacroInfo();
49 
50  // Bail out if the contents of the macro are containing keywords that are
51  // making the macro too complex.
52  if (std::find_if(
53  MI->tokens().begin(), MI->tokens().end(), [](const Token &T) {
54  return T.isOneOf(tok::kw_if, tok::kw_else, tok::kw_switch,
55  tok::kw_case, tok::kw_break, tok::kw_while,
56  tok::kw_do, tok::kw_for, tok::kw_continue,
57  tok::kw_goto, tok::kw_return);
58  }) != MI->tokens().end())
59  return;
60 
61  for (unsigned ArgNo = 0U; ArgNo < MI->getNumParams(); ++ArgNo) {
62  const IdentifierInfo *Arg = *(MI->param_begin() + ArgNo);
63  const Token *ResultArgToks = Args->getUnexpArgument(ArgNo);
64 
65  if (hasSideEffects(ResultArgToks) &&
66  countArgumentExpansions(MI, Arg) >= 2) {
67  Check.diag(ResultArgToks->getLocation(),
68  "side effects in the %ordinal0 macro argument %1 are "
69  "repeated in macro expansion")
70  << (ArgNo + 1) << Arg;
71  Check.diag(MI->getDefinitionLoc(), "macro %0 defined here",
72  DiagnosticIDs::Note)
73  << MacroNameTok.getIdentifierInfo();
74  }
75  }
76 }
77 
78 unsigned MacroRepeatedPPCallbacks::countArgumentExpansions(
79  const MacroInfo *MI, const IdentifierInfo *Arg) const {
80  // Current argument count. When moving forward to a different control-flow
81  // path this can decrease.
82  unsigned Current = 0;
83  // Max argument count.
84  unsigned Max = 0;
85  bool SkipParen = false;
86  int SkipParenCount = 0;
87  // Has a __builtin_constant_p been found?
88  bool FoundBuiltin = false;
89  bool PrevTokenIsHash = false;
90  // Count when "?" is reached. The "Current" will get this value when the ":"
91  // is reached.
92  std::stack<unsigned, SmallVector<unsigned, 8>> CountAtQuestion;
93  for (const auto &T : MI->tokens()) {
94  // The result of __builtin_constant_p(x) is 0 if x is a macro argument
95  // with side effects. If we see a __builtin_constant_p(x) followed by a
96  // "?" "&&" or "||", then we need to reason about control flow to report
97  // warnings correctly. Until such reasoning is added, bail out when this
98  // happens.
99  if (FoundBuiltin && T.isOneOf(tok::question, tok::ampamp, tok::pipepipe))
100  return Max;
101 
102  // Skip stringified tokens.
103  if (T.is(tok::hash)) {
104  PrevTokenIsHash = true;
105  continue;
106  }
107  if (PrevTokenIsHash) {
108  PrevTokenIsHash = false;
109  continue;
110  }
111 
112  // Handling of ? and :.
113  if (T.is(tok::question)) {
114  CountAtQuestion.push(Current);
115  } else if (T.is(tok::colon)) {
116  if (CountAtQuestion.empty())
117  return 0;
118  Current = CountAtQuestion.top();
119  CountAtQuestion.pop();
120  }
121 
122  // If current token is a parenthesis, skip it.
123  if (SkipParen) {
124  if (T.is(tok::l_paren))
125  SkipParenCount++;
126  else if (T.is(tok::r_paren))
127  SkipParenCount--;
128  SkipParen = (SkipParenCount != 0);
129  if (SkipParen)
130  continue;
131  }
132 
133  IdentifierInfo *TII = T.getIdentifierInfo();
134  // If not existent, skip it.
135  if (TII == nullptr)
136  continue;
137 
138  // If a __builtin_constant_p is found within the macro definition, don't
139  // count arguments inside the parentheses and remember that it has been
140  // seen in case there are "?", "&&" or "||" operators later.
141  if (TII->getBuiltinID() == Builtin::BI__builtin_constant_p) {
142  FoundBuiltin = true;
143  SkipParen = true;
144  continue;
145  }
146 
147  // If another macro is found within the macro definition, skip the macro
148  // and the eventual arguments.
149  if (TII->hasMacroDefinition()) {
150  const MacroInfo *M = PP.getMacroDefinition(TII).getMacroInfo();
151  if (M != nullptr && M->isFunctionLike())
152  SkipParen = true;
153  continue;
154  }
155 
156  // Count argument.
157  if (TII == Arg) {
158  Current++;
159  if (Current > Max)
160  Max = Current;
161  }
162  }
163  return Max;
164 }
165 
166 bool MacroRepeatedPPCallbacks::hasSideEffects(
167  const Token *ResultArgToks) const {
168  for (; ResultArgToks->isNot(tok::eof); ++ResultArgToks) {
169  if (ResultArgToks->isOneOf(tok::plusplus, tok::minusminus))
170  return true;
171  }
172  return false;
173 }
174 
176  const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
177  PP->addPPCallbacks(::std::make_unique<MacroRepeatedPPCallbacks>(*this, *PP));
178 }
179 
180 } // namespace bugprone
181 } // namespace tidy
182 } // namespace clang
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
clang::tidy::bugprone::MacroRepeatedSideEffectsCheck::registerPPCallbacks
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
Override this to register PPCallbacks in the preprocessor.
Definition: MacroRepeatedSideEffectsCheck.cpp:175
clang::doc::MD
static GeneratorRegistry::Add< MDGenerator > MD(MDGenerator::Format, "Generator for MD output.")
M
const google::protobuf::Message & M
Definition: Server.cpp:309
Args
llvm::json::Object Args
Definition: Trace.cpp:139
clang::tidy::bugprone::PP
static Preprocessor * PP
Definition: BadSignalToKillThreadCheck.cpp:29
PPCallbacks
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
SM
const SourceManager & SM
Definition: IncludeCleaner.cpp:140
MacroRepeatedSideEffectsCheck.h