Skip to content

Commit 5122738

Browse files
[clang-tidy] Support expressions of literals in modernize-macro-to-enum
Add a recursive descent parser to match macro expansion tokens against fully formed valid expressions of integral literals. Partial expressions will not be matched -- they can't be valid initializing expressions for an enum. Differential Revision: https://reviews.llvm.org/D124500 Fixes llvm#55055
1 parent bf8049d commit 5122738

File tree

8 files changed

+596
-74
lines changed

8 files changed

+596
-74
lines changed

clang-tools-extra/clang-tidy/modernize/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ add_clang_library(clangTidyModernizeModule
99
ConcatNestedNamespacesCheck.cpp
1010
DeprecatedHeadersCheck.cpp
1111
DeprecatedIosBaseAliasesCheck.cpp
12+
IntegralLiteralExpressionMatcher.cpp
1213
LoopConvertCheck.cpp
1314
LoopConvertUtils.cpp
1415
MacroToEnumCheck.cpp
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
//===--- IntegralLiteralExpressionMatcher.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 "IntegralLiteralExpressionMatcher.h"
10+
11+
#include <cctype>
12+
#include <stdexcept>
13+
14+
namespace clang {
15+
namespace tidy {
16+
namespace modernize {
17+
18+
// Validate that this literal token is a valid integer literal. A literal token
19+
// could be a floating-point token, which isn't acceptable as a value for an
20+
// enumeration. A floating-point token must either have a decimal point or an
21+
// exponent ('E' or 'P').
22+
static bool isIntegralConstant(const Token &Token) {
23+
const char *Begin = Token.getLiteralData();
24+
const char *End = Begin + Token.getLength();
25+
26+
// Not a hexadecimal floating-point literal.
27+
if (Token.getLength() > 2 && Begin[0] == '0' && std::toupper(Begin[1]) == 'X')
28+
return std::none_of(Begin + 2, End, [](char C) {
29+
return C == '.' || std::toupper(C) == 'P';
30+
});
31+
32+
// Not a decimal floating-point literal or complex literal.
33+
return std::none_of(Begin, End, [](char C) {
34+
return C == '.' || std::toupper(C) == 'E' || std::toupper(C) == 'I';
35+
});
36+
}
37+
38+
bool IntegralLiteralExpressionMatcher::advance() {
39+
++Current;
40+
return Current != End;
41+
}
42+
43+
bool IntegralLiteralExpressionMatcher::consume(tok::TokenKind Kind) {
44+
if (Current->is(Kind)) {
45+
++Current;
46+
return true;
47+
}
48+
49+
return false;
50+
}
51+
52+
bool IntegralLiteralExpressionMatcher::nonTerminalChainedExpr(
53+
bool (IntegralLiteralExpressionMatcher::*NonTerminal)(),
54+
const std::function<bool(Token)> &IsKind) {
55+
if (!(this->*NonTerminal)())
56+
return false;
57+
if (Current == End)
58+
return true;
59+
60+
while (Current != End) {
61+
if (!IsKind(*Current))
62+
break;
63+
64+
if (!advance())
65+
return false;
66+
67+
if (!(this->*NonTerminal)())
68+
return false;
69+
}
70+
71+
return true;
72+
}
73+
74+
// Advance over unary operators.
75+
bool IntegralLiteralExpressionMatcher::unaryOperator() {
76+
if (Current->isOneOf(tok::TokenKind::minus, tok::TokenKind::plus,
77+
tok::TokenKind::tilde, tok::TokenKind::exclaim)) {
78+
return advance();
79+
}
80+
81+
return true;
82+
}
83+
84+
bool IntegralLiteralExpressionMatcher::unaryExpr() {
85+
if (!unaryOperator())
86+
return false;
87+
88+
if (consume(tok::TokenKind::l_paren)) {
89+
if (Current == End)
90+
return false;
91+
92+
if (!expr())
93+
return false;
94+
95+
if (Current == End)
96+
return false;
97+
98+
return consume(tok::TokenKind::r_paren);
99+
}
100+
101+
if (!Current->isLiteral() || isStringLiteral(Current->getKind()) ||
102+
!isIntegralConstant(*Current)) {
103+
return false;
104+
}
105+
++Current;
106+
return true;
107+
}
108+
109+
bool IntegralLiteralExpressionMatcher::multiplicativeExpr() {
110+
return nonTerminalChainedExpr<tok::TokenKind::star, tok::TokenKind::slash,
111+
tok::TokenKind::percent>(
112+
&IntegralLiteralExpressionMatcher::unaryExpr);
113+
}
114+
115+
bool IntegralLiteralExpressionMatcher::additiveExpr() {
116+
return nonTerminalChainedExpr<tok::plus, tok::minus>(
117+
&IntegralLiteralExpressionMatcher::multiplicativeExpr);
118+
}
119+
120+
bool IntegralLiteralExpressionMatcher::shiftExpr() {
121+
return nonTerminalChainedExpr<tok::TokenKind::lessless,
122+
tok::TokenKind::greatergreater>(
123+
&IntegralLiteralExpressionMatcher::additiveExpr);
124+
}
125+
126+
bool IntegralLiteralExpressionMatcher::compareExpr() {
127+
if (!shiftExpr())
128+
return false;
129+
if (Current == End)
130+
return true;
131+
132+
if (Current->is(tok::TokenKind::spaceship)) {
133+
if (!advance())
134+
return false;
135+
136+
if (!shiftExpr())
137+
return false;
138+
}
139+
140+
return true;
141+
}
142+
143+
bool IntegralLiteralExpressionMatcher::relationalExpr() {
144+
return nonTerminalChainedExpr<tok::TokenKind::less, tok::TokenKind::greater,
145+
tok::TokenKind::lessequal,
146+
tok::TokenKind::greaterequal>(
147+
&IntegralLiteralExpressionMatcher::compareExpr);
148+
}
149+
150+
bool IntegralLiteralExpressionMatcher::equalityExpr() {
151+
return nonTerminalChainedExpr<tok::TokenKind::equalequal,
152+
tok::TokenKind::exclaimequal>(
153+
&IntegralLiteralExpressionMatcher::relationalExpr);
154+
}
155+
156+
bool IntegralLiteralExpressionMatcher::andExpr() {
157+
return nonTerminalChainedExpr<tok::TokenKind::amp>(
158+
&IntegralLiteralExpressionMatcher::equalityExpr);
159+
}
160+
161+
bool IntegralLiteralExpressionMatcher::exclusiveOrExpr() {
162+
return nonTerminalChainedExpr<tok::TokenKind::caret>(
163+
&IntegralLiteralExpressionMatcher::andExpr);
164+
}
165+
166+
bool IntegralLiteralExpressionMatcher::inclusiveOrExpr() {
167+
return nonTerminalChainedExpr<tok::TokenKind::pipe>(
168+
&IntegralLiteralExpressionMatcher::exclusiveOrExpr);
169+
}
170+
171+
bool IntegralLiteralExpressionMatcher::logicalAndExpr() {
172+
return nonTerminalChainedExpr<tok::TokenKind::ampamp>(
173+
&IntegralLiteralExpressionMatcher::inclusiveOrExpr);
174+
}
175+
176+
bool IntegralLiteralExpressionMatcher::logicalOrExpr() {
177+
return nonTerminalChainedExpr<tok::TokenKind::pipepipe>(
178+
&IntegralLiteralExpressionMatcher::logicalAndExpr);
179+
}
180+
181+
bool IntegralLiteralExpressionMatcher::conditionalExpr() {
182+
if (!logicalOrExpr())
183+
return false;
184+
if (Current == End)
185+
return true;
186+
187+
if (Current->is(tok::TokenKind::question)) {
188+
if (!advance())
189+
return false;
190+
191+
// A gcc extension allows x ? : y as a synonym for x ? x : y.
192+
if (Current->is(tok::TokenKind::colon)) {
193+
if (!advance())
194+
return false;
195+
196+
if (!expr())
197+
return false;
198+
199+
return true;
200+
}
201+
202+
if (!expr())
203+
return false;
204+
if (Current == End)
205+
return false;
206+
207+
if (!Current->is(tok::TokenKind::colon))
208+
return false;
209+
210+
if (!advance())
211+
return false;
212+
213+
if (!expr())
214+
return false;
215+
}
216+
return true;
217+
}
218+
219+
bool IntegralLiteralExpressionMatcher::commaExpr() {
220+
return nonTerminalChainedExpr<tok::TokenKind::comma>(
221+
&IntegralLiteralExpressionMatcher::conditionalExpr);
222+
}
223+
224+
bool IntegralLiteralExpressionMatcher::expr() { return commaExpr(); }
225+
226+
bool IntegralLiteralExpressionMatcher::match() {
227+
return expr() && Current == End;
228+
}
229+
230+
} // namespace modernize
231+
} // namespace tidy
232+
} // namespace clang
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
//===--- IntegralLiteralExpressionMatcher.h - 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+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_INTEGRAL_LITERAL_EXPRESSION_MATCHER_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_INTEGRAL_LITERAL_EXPRESSION_MATCHER_H
11+
12+
#include <clang/Lex/Token.h>
13+
#include <llvm/ADT/ArrayRef.h>
14+
15+
namespace clang {
16+
namespace tidy {
17+
namespace modernize {
18+
19+
// Parses an array of tokens and returns true if they conform to the rules of
20+
// C++ for whole expressions involving integral literals. Follows the operator
21+
// precedence rules of C++.
22+
class IntegralLiteralExpressionMatcher {
23+
public:
24+
IntegralLiteralExpressionMatcher(ArrayRef<Token> Tokens)
25+
: Current(Tokens.begin()), End(Tokens.end()) {}
26+
27+
bool match();
28+
29+
private:
30+
bool advance();
31+
bool consume(tok::TokenKind Kind);
32+
bool nonTerminalChainedExpr(
33+
bool (IntegralLiteralExpressionMatcher::*NonTerminal)(),
34+
const std::function<bool(Token)> &IsKind);
35+
template <tok::TokenKind Kind>
36+
bool nonTerminalChainedExpr(
37+
bool (IntegralLiteralExpressionMatcher::*NonTerminal)()) {
38+
return nonTerminalChainedExpr(NonTerminal,
39+
[](Token Tok) { return Tok.is(Kind); });
40+
}
41+
template <tok::TokenKind K1, tok::TokenKind K2, tok::TokenKind... Ks>
42+
bool nonTerminalChainedExpr(
43+
bool (IntegralLiteralExpressionMatcher::*NonTerminal)()) {
44+
return nonTerminalChainedExpr(
45+
NonTerminal, [](Token Tok) { return Tok.isOneOf(K1, K2, Ks...); });
46+
}
47+
48+
bool unaryOperator();
49+
bool unaryExpr();
50+
bool multiplicativeExpr();
51+
bool additiveExpr();
52+
bool shiftExpr();
53+
bool compareExpr();
54+
bool relationalExpr();
55+
bool equalityExpr();
56+
bool andExpr();
57+
bool exclusiveOrExpr();
58+
bool inclusiveOrExpr();
59+
bool logicalAndExpr();
60+
bool logicalOrExpr();
61+
bool conditionalExpr();
62+
bool commaExpr();
63+
bool expr();
64+
65+
ArrayRef<Token>::iterator Current;
66+
ArrayRef<Token>::iterator End;
67+
};
68+
69+
} // namespace modernize
70+
} // namespace tidy
71+
} // namespace clang
72+
73+
#endif

0 commit comments

Comments
 (0)