Skip to content

Commit 8170641

Browse files
committed
[clang-tidy] do not expand macros in modernize-use-using fix
Previously, the implementation used the printed type, which contains the macro arguments expanded (also deleting comments). Instead, this check can be more surgical and keep the actual written type as is, keeping macros unexpanded. Fixes #33760, #37846
1 parent eef3766 commit 8170641

File tree

4 files changed

+93
-18
lines changed

4 files changed

+93
-18
lines changed

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

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "UseUsingCheck.h"
10-
#include "clang/AST/ASTContext.h"
10+
#include "../utils/LexerUtils.h"
1111
#include "clang/AST/DeclGroup.h"
12+
#include "clang/Basic/SourceLocation.h"
13+
#include "clang/Basic/TokenKinds.h"
1214
#include "clang/Lex/Lexer.h"
15+
#include <string>
1316

1417
using namespace clang::ast_matchers;
1518
namespace {
@@ -119,14 +122,55 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
119122
return;
120123
}
121124

122-
PrintingPolicy PrintPolicy(getLangOpts());
123-
PrintPolicy.SuppressScope = true;
124-
PrintPolicy.ConstantArraySizeAsWritten = true;
125-
PrintPolicy.UseVoidForZeroParams = false;
126-
PrintPolicy.PrintInjectedClassNameWithArguments = false;
125+
const TypeLoc TL = MatchedDecl->getTypeSourceInfo()->getTypeLoc();
126+
127+
auto [Type, QualifierStr] = [MatchedDecl, &Result, this,
128+
&TL]() -> std::pair<std::string, std::string> {
129+
SourceRange TypeRange = TL.getSourceRange();
130+
131+
// Function pointer case, get the left and right side of the identifier
132+
// without the identifier.
133+
if (TypeRange.fullyContains(MatchedDecl->getLocation())) {
134+
return {(Lexer::getSourceText(
135+
CharSourceRange::getCharRange(TL.getBeginLoc(),
136+
MatchedDecl->getLocation()),
137+
*Result.SourceManager, getLangOpts()) +
138+
Lexer::getSourceText(
139+
CharSourceRange::getCharRange(
140+
Lexer::getLocForEndOfToken(MatchedDecl->getLocation(), 0,
141+
*Result.SourceManager,
142+
getLangOpts()),
143+
Lexer::getLocForEndOfToken(TL.getEndLoc(), 0,
144+
*Result.SourceManager,
145+
getLangOpts())),
146+
*Result.SourceManager, getLangOpts()))
147+
.str(),
148+
""};
149+
}
150+
151+
StringRef ExtraReference = "";
152+
if (MainTypeEndLoc.isValid() && TypeRange.fullyContains(MainTypeEndLoc)) {
153+
const SourceLocation Tok = utils::lexer::findPreviousAnyTokenKind(
154+
MatchedDecl->getLocation(), *Result.SourceManager, getLangOpts(),
155+
tok::TokenKind::star, tok::TokenKind::amp, tok::TokenKind::comma,
156+
tok::TokenKind::kw_typedef);
157+
158+
ExtraReference = Lexer::getSourceText(
159+
CharSourceRange::getCharRange(Tok, Tok.getLocWithOffset(1)),
160+
*Result.SourceManager, getLangOpts());
127161

128-
std::string Type = MatchedDecl->getUnderlyingType().getAsString(PrintPolicy);
129-
std::string Name = MatchedDecl->getNameAsString();
162+
if (ExtraReference != "*" && ExtraReference != "&")
163+
ExtraReference = "";
164+
165+
if (MainTypeEndLoc.isValid())
166+
TypeRange.setEnd(MainTypeEndLoc);
167+
}
168+
return {Lexer::getSourceText(CharSourceRange::getTokenRange(TypeRange),
169+
*Result.SourceManager, getLangOpts())
170+
.str(),
171+
ExtraReference.str()};
172+
}();
173+
StringRef Name = MatchedDecl->getName();
130174
SourceRange ReplaceRange = MatchedDecl->getSourceRange();
131175

132176
// typedefs with multiple comma-separated definitions produce multiple
@@ -143,7 +187,8 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
143187
// This is the first (and possibly the only) TypedefDecl in a typedef. Save
144188
// Type and Name in case we find subsequent TypedefDecl's in this typedef.
145189
FirstTypedefType = Type;
146-
FirstTypedefName = Name;
190+
FirstTypedefName = Name.str();
191+
MainTypeEndLoc = TL.getEndLoc();
147192
} else {
148193
// This is additional TypedefDecl in a comma-separated typedef declaration.
149194
// Start replacement *after* prior replacement and separate with semicolon.
@@ -153,10 +198,10 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
153198
// If this additional TypedefDecl's Type starts with the first TypedefDecl's
154199
// type, make this using statement refer back to the first type, e.g. make
155200
// "typedef int Foo, *Foo_p;" -> "using Foo = int;\nusing Foo_p = Foo*;"
156-
if (Type.size() > FirstTypedefType.size() &&
157-
Type.substr(0, FirstTypedefType.size()) == FirstTypedefType)
158-
Type = FirstTypedefName + Type.substr(FirstTypedefType.size() + 1);
201+
if (Type == FirstTypedefType && !QualifierStr.empty())
202+
Type = FirstTypedefName;
159203
}
204+
160205
if (!ReplaceRange.getEnd().isMacroID()) {
161206
const SourceLocation::IntTy Offset =
162207
MatchedDecl->getFunctionType() ? 0 : Name.size();
@@ -177,7 +222,7 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
177222
return;
178223
}
179224

180-
std::string Replacement = Using + Name + " = " + Type;
225+
std::string Replacement = (Using + Name + " = " + Type + QualifierStr).str();
181226
Diag << FixItHint::CreateReplacement(ReplaceRange, Replacement);
182227
}
183228
} // namespace clang::tidy::modernize

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class UseUsingCheck : public ClangTidyCheck {
2626

2727
std::string FirstTypedefType;
2828
std::string FirstTypedefName;
29+
SourceLocation MainTypeEndLoc;
2930

3031
public:
3132
UseUsingCheck(StringRef Name, ClangTidyContext *Context);

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,9 @@ Changes in existing checks
228228
member function calls too and to only expand macros starting with ``PRI``
229229
and ``__PRI`` from ``<inttypes.h>`` in the format string.
230230

231+
- Improved :doc:`modernize-use-using
232+
<clang-tidy/checks/modernize/use-using>` check by not expanding macros.
233+
231234
- Improved :doc:`performance-avoid-endl
232235
<clang-tidy/checks/performance/avoid-endl>` check to use ``std::endl`` as
233236
placeholder when lexer cannot get source text.

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

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ typedef Test<my_class *> another;
8080

8181
typedef int* PInt;
8282
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
83-
// CHECK-FIXES: using PInt = int *;
83+
// CHECK-FIXES: using PInt = int*;
8484

8585
typedef int bla1, bla2, bla3;
8686
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
@@ -112,7 +112,7 @@ TYPEDEF Foo Bak;
112112
typedef FOO Bam;
113113
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
114114
// CHECK-FIXES: #define FOO Foo
115-
// CHECK-FIXES: using Bam = Foo;
115+
// CHECK-FIXES: using Bam = FOO;
116116

117117
typedef struct Foo Bap;
118118
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
@@ -247,7 +247,7 @@ typedef Q<T{0 < 0}.b> Q3_t;
247247

248248
typedef TwoArgTemplate<TwoArgTemplate<int, Q<T{0 < 0}.b> >, S<(0 < 0), Q<b[0 < 0]> > > Nested_t;
249249
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
250-
// CHECK-FIXES: using Nested_t = TwoArgTemplate<TwoArgTemplate<int, Q<T{0 < 0}.b>>, S<(0 < 0), Q<b[0 < 0]>>>;
250+
// CHECK-FIXES: using Nested_t = TwoArgTemplate<TwoArgTemplate<int, Q<T{0 < 0}.b> >, S<(0 < 0), Q<b[0 < 0]> > >;
251251

252252
template <typename a>
253253
class TemplateKeyword {
@@ -265,12 +265,12 @@ class Variadic {};
265265

266266
typedef Variadic<Variadic<int, bool, Q<T{0 < 0}.b> >, S<(0 < 0), Variadic<Q<b[0 < 0]> > > > Variadic_t;
267267
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
268-
// CHECK-FIXES: using Variadic_t = Variadic<Variadic<int, bool, Q<T{0 < 0}.b>>, S<(0 < 0), Variadic<Q<b[0 < 0]>>>>
268+
// CHECK-FIXES: using Variadic_t = Variadic<Variadic<int, bool, Q<T{0 < 0}.b> >, S<(0 < 0), Variadic<Q<b[0 < 0]> > > >
269269

270270
typedef Variadic<Variadic<int, bool, Q<T{0 < 0}.b> >, S<(0 < 0), Variadic<Q<b[0 < 0]> > > > Variadic_t, *Variadic_p;
271271
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
272272
// CHECK-MESSAGES: :[[@LINE-2]]:103: warning: use 'using' instead of 'typedef'
273-
// CHECK-FIXES: using Variadic_t = Variadic<Variadic<int, bool, Q<T{0 < 0}.b>>, S<(0 < 0), Variadic<Q<b[0 < 0]>>>>;
273+
// CHECK-FIXES: using Variadic_t = Variadic<Variadic<int, bool, Q<T{0 < 0}.b> >, S<(0 < 0), Variadic<Q<b[0 < 0]> > > >;
274274
// CHECK-FIXES-NEXT: using Variadic_p = Variadic_t*;
275275

276276
typedef struct { int a; } R_t, *R_p;
@@ -383,3 +383,29 @@ namespace ISSUE_72179
383383
// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: use 'using' instead of 'typedef' [modernize-use-using]
384384
// CHECK-FIXES: const auto foo4 = [](int a){using d = int;};
385385
}
386+
387+
388+
typedef int* int_ptr, *int_ptr_ptr;
389+
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef' [modernize-use-using]
390+
// CHECK-MESSAGES: :[[@LINE-2]]:21: warning: use 'using' instead of 'typedef' [modernize-use-using]
391+
// CHECK-FIXES: using int_ptr = int*;
392+
// CHECK-FIXES-NEXT: using int_ptr_ptr = int_ptr*;
393+
394+
#ifndef SpecialMode
395+
#define SomeMacro(x) x
396+
#else
397+
#define SomeMacro(x) SpecialType
398+
#endif
399+
400+
class SomeMacro(GH33760) { };
401+
402+
typedef void(SomeMacro(GH33760)::* FunctionType)(float, int);
403+
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef' [modernize-use-using]
404+
// CHECK-FIXES: using FunctionType = void(SomeMacro(GH33760)::* )(float, int);
405+
406+
#define CDECL __attribute((cdecl))
407+
408+
typedef void (CDECL *GH37846)(int);
409+
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef' [modernize-use-using]
410+
// CHECK-FIXES: using GH37846 = void (CDECL *)(int);
411+

0 commit comments

Comments
 (0)