Skip to content

Commit 782392d

Browse files
author
Mitchell Balan
committed
[clang-tidy] modernize-use-using work with multi-argument templates
Summary: If clang-tidy's modernize-use-using feature finds any commas that are not within parentheses, it won't create a fix. That means it won't change lines like: typedef std::pair<int, int> Point; to using Point = std::pair<int, int>; or even: typedef std::map<std::string, Foo> MyMap; typedef std::vector<int,MyCustomAllocator<int>> MyVector; This patch allows the fix to apply to lines with commas if they are within parentheses or angle brackets that were not themselves within parentheses. Reviewers: alexfh, hokein, aaron.ballman Patch by: poelmanc Subscribers: jonathanmeier, cfe-commits Tags: #clang, #clang-tools-extra Differential Revision: https://reviews.llvm.org/D67460
1 parent 9c1baa2 commit 782392d

File tree

2 files changed

+95
-11
lines changed

2 files changed

+95
-11
lines changed

clang-tools-extra/clang-tidy/modernize/UseUsingCheck.cpp

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,25 +39,46 @@ static bool CheckRemoval(SourceManager &SM, SourceLocation StartLoc,
3939
File.begin(), TokenBegin, File.end());
4040

4141
Token Tok;
42-
int ParenLevel = 0;
42+
int NestingLevel = 0; // Parens, braces, and square brackets
43+
int AngleBracketLevel = 0;
4344
bool FoundTypedef = false;
4445

4546
while (!DeclLexer.LexFromRawLexer(Tok) && !Tok.is(tok::semi)) {
4647
switch (Tok.getKind()) {
4748
case tok::l_brace:
48-
case tok::r_brace:
49-
// This might be the `typedef struct {...} T;` case.
50-
return false;
49+
if (NestingLevel == 0 && AngleBracketLevel == 0) {
50+
// At top level, this might be the `typedef struct {...} T;` case.
51+
// Inside parens, square brackets, or angle brackets it's not.
52+
return false;
53+
}
54+
++NestingLevel;
55+
break;
5156
case tok::l_paren:
52-
ParenLevel++;
57+
case tok::l_square:
58+
++NestingLevel;
5359
break;
60+
case tok::r_brace:
5461
case tok::r_paren:
55-
ParenLevel--;
62+
case tok::r_square:
63+
--NestingLevel;
64+
break;
65+
case tok::less:
66+
// If not nested in paren/brace/square bracket, treat as opening angle bracket.
67+
if (NestingLevel == 0)
68+
++AngleBracketLevel;
69+
break;
70+
case tok::greater:
71+
// Per C++ 17 Draft N4659, Section 17.2/3
72+
// https://timsong-cpp.github.io/cppwp/n4659/temp.names#3:
73+
// "When parsing a template-argument-list, the first non-nested > is
74+
// taken as the ending delimiter rather than a greater-than operator."
75+
// If not nested in paren/brace/square bracket, treat as closing angle bracket.
76+
if (NestingLevel == 0)
77+
--AngleBracketLevel;
5678
break;
5779
case tok::comma:
58-
if (ParenLevel == 0) {
59-
// If there is comma and we are not between open parenthesis then it is
60-
// two or more declarations in this chain.
80+
if (NestingLevel == 0 && AngleBracketLevel == 0) {
81+
// If there is a non-nested comma we have two or more declarations in this chain.
6182
return false;
6283
}
6384
break;
@@ -88,8 +109,7 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
88109
if (StartLoc.isMacroID() && IgnoreMacros)
89110
return;
90111

91-
auto Diag =
92-
diag(StartLoc, "use 'using' instead of 'typedef'");
112+
auto Diag = diag(StartLoc, "use 'using' instead of 'typedef'");
93113

94114
// do not fix if there is macro or array
95115
if (MatchedDecl->getUnderlyingType()->isArrayType() || StartLoc.isMacroID())

clang-tools-extra/test/clang-tidy/checkers/modernize-use-using.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,67 @@ class E : public C<E> {
183183
void f() override { super::f(); }
184184
};
185185
}
186+
187+
template <typename T1, typename T2>
188+
class TwoArgTemplate {
189+
typedef TwoArgTemplate<T1, T2> self;
190+
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'using' instead of 'typedef'
191+
// CHECK-FIXES: using self = TwoArgTemplate<T1, T2>;
192+
};
193+
194+
template <bool B, typename T>
195+
struct S {};
196+
197+
typedef S<(0 > 0), int> S_t, *S_p;
198+
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
199+
// CHECK-FIXES: typedef S<(0 > 0), int> S_t, *S_p;
200+
201+
typedef S<(0 < 0), int> S2_t, *S2_p;
202+
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
203+
// CHECK-FIXES: typedef S<(0 < 0), int> S2_t, *S2_p;
204+
205+
typedef S<(0 > 0 && (3 > 1) && (1 < 1)), int> S3_t;
206+
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
207+
// CHECK-FIXES: using S3_t = S<(0 > 0 && (3 > 1) && (1 < 1)), int>;
208+
209+
template <bool B>
210+
struct Q {};
211+
212+
constexpr bool b[1] = {true};
213+
214+
typedef Q<b[0 < 0]> Q_t, *Q_p;
215+
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
216+
// CHECK-FIXES: typedef Q<b[0 < 0]> Q_t, *Q_p;
217+
218+
typedef Q<b[0 < 0]> Q2_t;
219+
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
220+
// CHECK-FIXES: using Q2_t = Q<b[0 < 0]>;
221+
222+
struct T {
223+
constexpr T(bool) {}
224+
225+
static constexpr bool b = true;
226+
};
227+
228+
typedef Q<T{0 < 0}.b> Q3_t, *Q3_p;
229+
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
230+
// CHECK-FIXES: typedef Q<T{0 < 0}.b> Q3_t, *Q3_p;
231+
232+
typedef Q<T{0 < 0}.b> Q3_t;
233+
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
234+
// CHECK-FIXES: using Q3_t = Q<T{0 < 0}.b>;
235+
236+
typedef TwoArgTemplate<TwoArgTemplate<int, Q<T{0 < 0}.b> >, S<(0 < 0), Q<b[0 < 0]> > > Nested_t;
237+
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
238+
// CHECK-FIXES: using Nested_t = TwoArgTemplate<TwoArgTemplate<int, Q<T{0 < 0}.b> >, S<(0 < 0), Q<b[0 < 0]> > >;
239+
240+
template <typename... Args>
241+
class Variadic {};
242+
243+
typedef Variadic<Variadic<int, bool, Q<T{0 < 0}.b> >, S<(0 < 0), Variadic<Q<b[0 < 0]> > > > Variadic_t;
244+
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
245+
// CHECK-FIXES: using Variadic_t = Variadic<Variadic<int, bool, Q<T{0 < 0}.b> >, S<(0 < 0), Variadic<Q<b[0 < 0]> > > >
246+
247+
typedef Variadic<Variadic<int, bool, Q<T{0 < 0}.b> >, S<(0 < 0), Variadic<Q<b[0 < 0]> > > > Variadic_t, *Variadic_p;
248+
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
249+
// CHECK-FIXES: typedef Variadic<Variadic<int, bool, Q<T{0 < 0}.b> >, S<(0 < 0), Variadic<Q<b[0 < 0]> > > > Variadic_t, *Variadic_p;

0 commit comments

Comments
 (0)