150
|
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
|
|
9 #include "MacroRepeatedSideEffectsCheck.h"
|
|
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.
|
236
|
52 if (llvm::any_of(MI->tokens(), [](const Token &T) {
|
|
53 return T.isOneOf(tok::kw_if, tok::kw_else, tok::kw_switch, tok::kw_case,
|
|
54 tok::kw_break, tok::kw_while, tok::kw_do, tok::kw_for,
|
|
55 tok::kw_continue, tok::kw_goto, tok::kw_return);
|
|
56 }))
|
150
|
57 return;
|
|
58
|
|
59 for (unsigned ArgNo = 0U; ArgNo < MI->getNumParams(); ++ArgNo) {
|
|
60 const IdentifierInfo *Arg = *(MI->param_begin() + ArgNo);
|
|
61 const Token *ResultArgToks = Args->getUnexpArgument(ArgNo);
|
|
62
|
|
63 if (hasSideEffects(ResultArgToks) &&
|
|
64 countArgumentExpansions(MI, Arg) >= 2) {
|
|
65 Check.diag(ResultArgToks->getLocation(),
|
|
66 "side effects in the %ordinal0 macro argument %1 are "
|
|
67 "repeated in macro expansion")
|
|
68 << (ArgNo + 1) << Arg;
|
|
69 Check.diag(MI->getDefinitionLoc(), "macro %0 defined here",
|
|
70 DiagnosticIDs::Note)
|
|
71 << MacroNameTok.getIdentifierInfo();
|
|
72 }
|
|
73 }
|
|
74 }
|
|
75
|
|
76 unsigned MacroRepeatedPPCallbacks::countArgumentExpansions(
|
|
77 const MacroInfo *MI, const IdentifierInfo *Arg) const {
|
|
78 // Current argument count. When moving forward to a different control-flow
|
|
79 // path this can decrease.
|
|
80 unsigned Current = 0;
|
|
81 // Max argument count.
|
|
82 unsigned Max = 0;
|
|
83 bool SkipParen = false;
|
|
84 int SkipParenCount = 0;
|
|
85 // Has a __builtin_constant_p been found?
|
|
86 bool FoundBuiltin = false;
|
|
87 bool PrevTokenIsHash = false;
|
|
88 // Count when "?" is reached. The "Current" will get this value when the ":"
|
|
89 // is reached.
|
|
90 std::stack<unsigned, SmallVector<unsigned, 8>> CountAtQuestion;
|
|
91 for (const auto &T : MI->tokens()) {
|
|
92 // The result of __builtin_constant_p(x) is 0 if x is a macro argument
|
|
93 // with side effects. If we see a __builtin_constant_p(x) followed by a
|
|
94 // "?" "&&" or "||", then we need to reason about control flow to report
|
|
95 // warnings correctly. Until such reasoning is added, bail out when this
|
|
96 // happens.
|
|
97 if (FoundBuiltin && T.isOneOf(tok::question, tok::ampamp, tok::pipepipe))
|
|
98 return Max;
|
|
99
|
|
100 // Skip stringified tokens.
|
|
101 if (T.is(tok::hash)) {
|
|
102 PrevTokenIsHash = true;
|
|
103 continue;
|
|
104 }
|
|
105 if (PrevTokenIsHash) {
|
|
106 PrevTokenIsHash = false;
|
|
107 continue;
|
|
108 }
|
|
109
|
|
110 // Handling of ? and :.
|
|
111 if (T.is(tok::question)) {
|
|
112 CountAtQuestion.push(Current);
|
|
113 } else if (T.is(tok::colon)) {
|
|
114 if (CountAtQuestion.empty())
|
|
115 return 0;
|
|
116 Current = CountAtQuestion.top();
|
|
117 CountAtQuestion.pop();
|
|
118 }
|
|
119
|
|
120 // If current token is a parenthesis, skip it.
|
|
121 if (SkipParen) {
|
|
122 if (T.is(tok::l_paren))
|
|
123 SkipParenCount++;
|
|
124 else if (T.is(tok::r_paren))
|
|
125 SkipParenCount--;
|
|
126 SkipParen = (SkipParenCount != 0);
|
|
127 if (SkipParen)
|
|
128 continue;
|
|
129 }
|
|
130
|
|
131 IdentifierInfo *TII = T.getIdentifierInfo();
|
|
132 // If not existent, skip it.
|
|
133 if (TII == nullptr)
|
|
134 continue;
|
|
135
|
|
136 // If a __builtin_constant_p is found within the macro definition, don't
|
|
137 // count arguments inside the parentheses and remember that it has been
|
|
138 // seen in case there are "?", "&&" or "||" operators later.
|
|
139 if (TII->getBuiltinID() == Builtin::BI__builtin_constant_p) {
|
|
140 FoundBuiltin = true;
|
|
141 SkipParen = true;
|
|
142 continue;
|
|
143 }
|
|
144
|
|
145 // If another macro is found within the macro definition, skip the macro
|
|
146 // and the eventual arguments.
|
|
147 if (TII->hasMacroDefinition()) {
|
|
148 const MacroInfo *M = PP.getMacroDefinition(TII).getMacroInfo();
|
|
149 if (M != nullptr && M->isFunctionLike())
|
|
150 SkipParen = true;
|
|
151 continue;
|
|
152 }
|
|
153
|
|
154 // Count argument.
|
|
155 if (TII == Arg) {
|
|
156 Current++;
|
|
157 if (Current > Max)
|
|
158 Max = Current;
|
|
159 }
|
|
160 }
|
|
161 return Max;
|
|
162 }
|
|
163
|
|
164 bool MacroRepeatedPPCallbacks::hasSideEffects(
|
|
165 const Token *ResultArgToks) const {
|
|
166 for (; ResultArgToks->isNot(tok::eof); ++ResultArgToks) {
|
|
167 if (ResultArgToks->isOneOf(tok::plusplus, tok::minusminus))
|
|
168 return true;
|
|
169 }
|
|
170 return false;
|
|
171 }
|
|
172
|
|
173 void MacroRepeatedSideEffectsCheck::registerPPCallbacks(
|
|
174 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
|
|
175 PP->addPPCallbacks(::std::make_unique<MacroRepeatedPPCallbacks>(*this, *PP));
|
|
176 }
|
|
177
|
|
178 } // namespace bugprone
|
|
179 } // namespace tidy
|
|
180 } // namespace clang
|