Skip to content

Commit f77152d

Browse files
authored
[clang-tidy] use specified type verbatim in modernize-use-using fix (llvm#113837)
Previously, the implementation used the printed type, which contains expanded macro arguments, deletes comments, and removes function argument names from the alias declaration. Instead, this check can be more surgical and use the actual written type verbatim. Fixes llvm#33760 Fixes llvm#37846 Fixes llvm#41685 Fixes llvm#83568 Fixes llvm#95716 Fixes llvm#97009
1 parent b9a2658 commit f77152d

File tree

4 files changed

+123
-20
lines changed

4 files changed

+123
-20
lines changed

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

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,14 @@
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/LangOptions.h"
13+
#include "clang/Basic/SourceLocation.h"
14+
#include "clang/Basic/SourceManager.h"
15+
#include "clang/Basic/TokenKinds.h"
1216
#include "clang/Lex/Lexer.h"
17+
#include <string>
1318

1419
using namespace clang::ast_matchers;
1520
namespace {
@@ -83,6 +88,9 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
8388
if (!ParentDecl)
8489
return;
8590

91+
const SourceManager &SM = *Result.SourceManager;
92+
const LangOptions &LO = getLangOpts();
93+
8694
// Match CXXRecordDecl only to store the range of the last non-implicit full
8795
// declaration, to later check whether it's within the typdef itself.
8896
const auto *MatchedTagDecl = Result.Nodes.getNodeAs<TagDecl>(TagDeclName);
@@ -119,14 +127,51 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
119127
return;
120128
}
121129

122-
PrintingPolicy PrintPolicy(getLangOpts());
123-
PrintPolicy.SuppressScope = true;
124-
PrintPolicy.ConstantArraySizeAsWritten = true;
125-
PrintPolicy.UseVoidForZeroParams = false;
126-
PrintPolicy.PrintInjectedClassNameWithArguments = false;
130+
const TypeLoc TL = MatchedDecl->getTypeSourceInfo()->getTypeLoc();
131+
132+
auto [Type, QualifierStr] = [MatchedDecl, this, &TL, &SM,
133+
&LO]() -> std::pair<std::string, std::string> {
134+
SourceRange TypeRange = TL.getSourceRange();
135+
136+
// Function pointer case, get the left and right side of the identifier
137+
// without the identifier.
138+
if (TypeRange.fullyContains(MatchedDecl->getLocation())) {
139+
const auto RangeLeftOfIdentifier = CharSourceRange::getCharRange(
140+
TypeRange.getBegin(), MatchedDecl->getLocation());
141+
const auto RangeRightOfIdentifier = CharSourceRange::getCharRange(
142+
Lexer::getLocForEndOfToken(MatchedDecl->getLocation(), 0, SM, LO),
143+
Lexer::getLocForEndOfToken(TypeRange.getEnd(), 0, SM, LO));
144+
const std::string VerbatimType =
145+
(Lexer::getSourceText(RangeLeftOfIdentifier, SM, LO) +
146+
Lexer::getSourceText(RangeRightOfIdentifier, SM, LO))
147+
.str();
148+
return {VerbatimType, ""};
149+
}
150+
151+
StringRef ExtraReference = "";
152+
if (MainTypeEndLoc.isValid() && TypeRange.fullyContains(MainTypeEndLoc)) {
153+
// Each type introduced in a typedef can specify being a reference or
154+
// pointer type seperately, so we need to sigure out if the new using-decl
155+
// needs to be to a reference or pointer as well.
156+
const SourceLocation Tok = utils::lexer::findPreviousAnyTokenKind(
157+
MatchedDecl->getLocation(), SM, LO, tok::TokenKind::star,
158+
tok::TokenKind::amp, tok::TokenKind::comma,
159+
tok::TokenKind::kw_typedef);
160+
161+
ExtraReference = Lexer::getSourceText(
162+
CharSourceRange::getCharRange(Tok, Tok.getLocWithOffset(1)), SM, LO);
127163

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

132177
// typedefs with multiple comma-separated definitions produce multiple
@@ -143,7 +188,8 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
143188
// This is the first (and possibly the only) TypedefDecl in a typedef. Save
144189
// Type and Name in case we find subsequent TypedefDecl's in this typedef.
145190
FirstTypedefType = Type;
146-
FirstTypedefName = Name;
191+
FirstTypedefName = Name.str();
192+
MainTypeEndLoc = TL.getEndLoc();
147193
} else {
148194
// This is additional TypedefDecl in a comma-separated typedef declaration.
149195
// Start replacement *after* prior replacement and separate with semicolon.
@@ -153,10 +199,10 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
153199
// If this additional TypedefDecl's Type starts with the first TypedefDecl's
154200
// type, make this using statement refer back to the first type, e.g. make
155201
// "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);
202+
if (Type == FirstTypedefType && !QualifierStr.empty())
203+
Type = FirstTypedefName;
159204
}
205+
160206
if (!ReplaceRange.getEnd().isMacroID()) {
161207
const SourceLocation::IntTy Offset =
162208
MatchedDecl->getFunctionType() ? 0 : Name.size();
@@ -171,13 +217,12 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
171217
LastTagDeclRange->second.isValid() &&
172218
ReplaceRange.fullyContains(LastTagDeclRange->second)) {
173219
Type = std::string(Lexer::getSourceText(
174-
CharSourceRange::getTokenRange(LastTagDeclRange->second),
175-
*Result.SourceManager, getLangOpts()));
220+
CharSourceRange::getTokenRange(LastTagDeclRange->second), SM, LO));
176221
if (Type.empty())
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
@@ -317,6 +317,9 @@ Changes in existing checks
317317
member function calls too and to only expand macros starting with ``PRI``
318318
and ``__PRI`` from ``<inttypes.h>`` in the format string.
319319

320+
- Improved :doc:`modernize-use-using
321+
<clang-tidy/checks/modernize/use-using>` check by not expanding macros.
322+
320323
- Improved :doc:`performance-avoid-endl
321324
<clang-tidy/checks/performance/avoid-endl>` check to use ``std::endl`` as
322325
placeholder when lexer cannot get source text.

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

Lines changed: 59 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,57 @@ 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+
// GH37846 & GH41685
409+
typedef void (CDECL *GH37846)(int);
410+
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef' [modernize-use-using]
411+
// CHECK-FIXES: using GH37846 = void (CDECL *)(int);
412+
413+
typedef void (__attribute((cdecl)) *GH41685)(int);
414+
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef' [modernize-use-using]
415+
// CHECK-FIXES: using GH41685 = void (__attribute((cdecl)) *)(int);
416+
417+
namespace GH83568 {
418+
typedef int(*name)(int arg1, int arg2);
419+
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'using' instead of 'typedef' [modernize-use-using]
420+
// CHECK-FIXES: using name = int(*)(int arg1, int arg2);
421+
}
422+
423+
#ifdef FOO
424+
#define GH95716 float
425+
#else
426+
#define GH95716 double
427+
#endif
428+
429+
typedef GH95716 foo;
430+
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef' [modernize-use-using]
431+
// CHECK-FIXES: using foo = GH95716;
432+
433+
namespace GH97009 {
434+
typedef double PointType[3];
435+
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'using' instead of 'typedef' [modernize-use-using]
436+
typedef bool (*Function)(PointType, PointType);
437+
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'using' instead of 'typedef' [modernize-use-using]
438+
// CHECK-FIXES: using Function = bool (*)(PointType, PointType);
439+
}

0 commit comments

Comments
 (0)