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