annotate clang-tools-extra/clang-tidy/bugprone/MacroRepeatedSideEffectsCheck.cpp @ 236:c4bab56944e8 llvm-original

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