7
7
// ===----------------------------------------------------------------------===//
8
8
9
9
#include " UseUsingCheck.h"
10
- #include " ../utils/LexerUtils.h"
11
- #include " clang/AST/DeclGroup.h"
10
+ #include " clang/Basic/Diagnostic.h"
12
11
#include " clang/Basic/LangOptions.h"
13
12
#include " clang/Basic/SourceLocation.h"
14
13
#include " clang/Basic/SourceManager.h"
15
14
#include " clang/Basic/TokenKinds.h"
16
15
#include " clang/Lex/Lexer.h"
17
- #include < string>
18
16
19
17
using namespace clang ::ast_matchers;
20
18
namespace {
21
19
22
20
AST_MATCHER (clang::LinkageSpecDecl, isExternCLinkage) {
23
21
return Node.getLanguage () == clang::LinkageSpecLanguageIDs::C;
24
22
}
23
+
25
24
} // namespace
26
25
27
26
namespace clang ::tidy::modernize {
28
27
29
28
static constexpr llvm::StringLiteral ExternCDeclName = " extern-c-decl" ;
30
- static constexpr llvm::StringLiteral ParentDeclName = " parent-decl" ;
31
- static constexpr llvm::StringLiteral TagDeclName = " tag-decl" ;
32
29
static constexpr llvm::StringLiteral TypedefName = " typedef" ;
33
- static constexpr llvm::StringLiteral DeclStmtName = " decl-stmt" ;
34
30
35
31
UseUsingCheck::UseUsingCheck (StringRef Name, ClangTidyContext *Context)
36
32
: ClangTidyCheck(Name, Context),
@@ -47,183 +43,65 @@ void UseUsingCheck::registerMatchers(MatchFinder *Finder) {
47
43
typedefDecl (
48
44
unless (isInstantiated ()),
49
45
optionally (hasAncestor (
50
- linkageSpecDecl (isExternCLinkage ()).bind (ExternCDeclName))),
51
- anyOf (hasParent (decl ().bind (ParentDeclName)),
52
- hasParent (declStmt ().bind (DeclStmtName))))
46
+ linkageSpecDecl (isExternCLinkage ()).bind (ExternCDeclName))))
53
47
.bind (TypedefName),
54
48
this );
55
-
56
- // This matcher is used to find tag declarations in source code within
57
- // typedefs. They appear in the AST just *prior* to the typedefs.
58
- Finder->addMatcher (
59
- tagDecl (
60
- anyOf (allOf (unless (anyOf (isImplicit (),
61
- classTemplateSpecializationDecl ())),
62
- anyOf (hasParent (decl ().bind (ParentDeclName)),
63
- hasParent (declStmt ().bind (DeclStmtName)))),
64
- // We want the parent of the ClassTemplateDecl, not the parent
65
- // of the specialization.
66
- classTemplateSpecializationDecl (hasAncestor (classTemplateDecl (
67
- anyOf (hasParent (decl ().bind (ParentDeclName)),
68
- hasParent (declStmt ().bind (DeclStmtName))))))))
69
- .bind (TagDeclName),
70
- this );
71
49
}
72
50
73
51
void UseUsingCheck::check (const MatchFinder::MatchResult &Result) {
74
- const auto *ParentDecl = Result.Nodes .getNodeAs <Decl>(ParentDeclName);
75
-
76
- if (!ParentDecl) {
77
- const auto *ParentDeclStmt = Result.Nodes .getNodeAs <DeclStmt>(DeclStmtName);
78
- if (ParentDeclStmt) {
79
- if (ParentDeclStmt->isSingleDecl ())
80
- ParentDecl = ParentDeclStmt->getSingleDecl ();
81
- else
82
- ParentDecl =
83
- ParentDeclStmt->getDeclGroup ().getDeclGroup ()
84
- [ParentDeclStmt->getDeclGroup ().getDeclGroup ().size () - 1 ];
85
- }
86
- }
87
-
88
- if (!ParentDecl)
89
- return ;
90
-
91
- const SourceManager &SM = *Result.SourceManager ;
92
- const LangOptions &LO = getLangOpts ();
93
-
94
- // Match CXXRecordDecl only to store the range of the last non-implicit full
95
- // declaration, to later check whether it's within the typdef itself.
96
- const auto *MatchedTagDecl = Result.Nodes .getNodeAs <TagDecl>(TagDeclName);
97
- if (MatchedTagDecl) {
98
- // It is not sufficient to just track the last TagDecl that we've seen,
99
- // because if one struct or union is nested inside another, the last TagDecl
100
- // before the typedef will be the nested one (PR#50990). Therefore, we also
101
- // keep track of the parent declaration, so that we can look up the last
102
- // TagDecl that is a sibling of the typedef in the AST.
103
- if (MatchedTagDecl->isThisDeclarationADefinition ())
104
- LastTagDeclRanges[ParentDecl] = MatchedTagDecl->getSourceRange ();
105
- return ;
106
- }
107
-
108
- const auto *MatchedDecl = Result.Nodes .getNodeAs <TypedefDecl>(TypedefName);
109
- if (MatchedDecl->getLocation ().isInvalid ())
52
+ const auto &MatchedDecl = *Result.Nodes .getNodeAs <TypedefDecl>(TypedefName);
53
+ if (MatchedDecl.getLocation ().isInvalid ())
110
54
return ;
111
55
112
56
const auto *ExternCDecl =
113
57
Result.Nodes .getNodeAs <LinkageSpecDecl>(ExternCDeclName);
114
58
if (ExternCDecl && IgnoreExternC)
115
59
return ;
116
60
117
- SourceLocation StartLoc = MatchedDecl->getBeginLoc ();
118
-
119
- if (StartLoc.isMacroID () && IgnoreMacros)
120
- return ;
121
-
122
61
static constexpr llvm::StringLiteral UseUsingWarning =
123
62
" use 'using' instead of 'typedef'" ;
124
63
125
- // Warn at StartLoc but do not fix if there is macro or array.
126
- if (MatchedDecl->getUnderlyingType ()->isArrayType () || StartLoc.isMacroID ()) {
127
- diag (StartLoc, UseUsingWarning);
64
+ if (MatchedDecl.getBeginLoc ().isMacroID ()) {
65
+ // Warn but do not fix if there is a macro.
66
+ if (!IgnoreMacros)
67
+ diag (MatchedDecl.getBeginLoc (), UseUsingWarning);
128
68
return ;
129
69
}
130
70
131
- const TypeLoc TL = MatchedDecl->getTypeSourceInfo ()->getTypeLoc ();
132
-
133
- auto [Type, QualifierStr] = [MatchedDecl, this , &TL, &SM,
134
- &LO]() -> std::pair<std::string, std::string> {
135
- SourceRange TypeRange = TL.getSourceRange ();
136
-
137
- // Function pointer case, get the left and right side of the identifier
138
- // without the identifier.
139
- if (TypeRange.fullyContains (MatchedDecl->getLocation ())) {
140
- const auto RangeLeftOfIdentifier = CharSourceRange::getCharRange (
141
- TypeRange.getBegin (), MatchedDecl->getLocation ());
142
- const auto RangeRightOfIdentifier = CharSourceRange::getCharRange (
143
- Lexer::getLocForEndOfToken (MatchedDecl->getLocation (), 0 , SM, LO),
144
- Lexer::getLocForEndOfToken (TypeRange.getEnd (), 0 , SM, LO));
145
- const std::string VerbatimType =
146
- (Lexer::getSourceText (RangeLeftOfIdentifier, SM, LO) +
147
- Lexer::getSourceText (RangeRightOfIdentifier, SM, LO))
148
- .str ();
149
- return {VerbatimType, " " };
150
- }
151
-
152
- StringRef ExtraReference = " " ;
153
- if (MainTypeEndLoc.isValid () && TypeRange.fullyContains (MainTypeEndLoc)) {
154
- // Each type introduced in a typedef can specify being a reference or
155
- // pointer type seperately, so we need to sigure out if the new using-decl
156
- // needs to be to a reference or pointer as well.
157
- const SourceLocation Tok = utils::lexer::findPreviousAnyTokenKind (
158
- MatchedDecl->getLocation (), SM, LO, tok::TokenKind::star,
159
- tok::TokenKind::amp, tok::TokenKind::comma,
160
- tok::TokenKind::kw_typedef);
161
-
162
- ExtraReference = Lexer::getSourceText (
163
- CharSourceRange::getCharRange (Tok, Tok.getLocWithOffset (1 )), SM, LO);
164
-
165
- if (ExtraReference != " *" && ExtraReference != " &" )
166
- ExtraReference = " " ;
167
-
168
- TypeRange.setEnd (MainTypeEndLoc);
169
- }
170
- return {
171
- Lexer::getSourceText (CharSourceRange::getTokenRange (TypeRange), SM, LO)
172
- .str (),
173
- ExtraReference.str ()};
174
- }();
175
- StringRef Name = MatchedDecl->getName ();
176
- SourceRange ReplaceRange = MatchedDecl->getSourceRange ();
71
+ const SourceManager &SM = *Result.SourceManager ;
72
+ const LangOptions &LO = getLangOpts ();
177
73
178
74
// typedefs with multiple comma-separated definitions produce multiple
179
75
// consecutive TypedefDecl nodes whose SourceRanges overlap. Each range starts
180
76
// at the "typedef" and then continues *across* previous definitions through
181
77
// the end of the current TypedefDecl definition.
182
- // But also we need to check that the ranges belong to the same file because
183
- // different files may contain overlapping ranges.
184
- std::string Using = " using " ;
185
- if (ReplaceRange.getBegin ().isMacroID () ||
186
- (Result.SourceManager ->getFileID (ReplaceRange.getBegin ()) !=
187
- Result.SourceManager ->getFileID (LastReplacementEnd)) ||
188
- (ReplaceRange.getBegin () >= LastReplacementEnd)) {
189
- // This is the first (and possibly the only) TypedefDecl in a typedef. Save
190
- // Type and Name in case we find subsequent TypedefDecl's in this typedef.
191
- FirstTypedefType = Type;
192
- FirstTypedefName = Name.str ();
193
- MainTypeEndLoc = TL.getEndLoc ();
78
+ const SourceRange RemovalRange = {
79
+ Lexer::findPreviousToken (MatchedDecl.getLocation (), SM, LO,
80
+ /* IncludeComments=*/ true )
81
+ ->getEndLoc (),
82
+ Lexer::getLocForEndOfToken (MatchedDecl.getLocation (), 1 , SM, LO)};
83
+ if (NextTypedefStartsANewSequence) {
84
+ diag (MatchedDecl.getBeginLoc (), UseUsingWarning)
85
+ << FixItHint::CreateReplacement (
86
+ {MatchedDecl.getBeginLoc (),
87
+ Lexer::getLocForEndOfToken (MatchedDecl.getBeginLoc (), 0 , SM,
88
+ LO)},
89
+ (" using " + MatchedDecl.getName () + " =" ).str ())
90
+ << FixItHint::CreateRemoval (RemovalRange);
91
+ FirstTypedefName = MatchedDecl.getName ();
194
92
} else {
195
- // This is additional TypedefDecl in a comma-separated typedef declaration.
196
- // Start replacement *after* prior replacement and separate with semicolon.
197
- ReplaceRange.setBegin (LastReplacementEnd);
198
- Using = " ;\n using " ;
199
-
200
- // If this additional TypedefDecl's Type starts with the first TypedefDecl's
201
- // type, make this using statement refer back to the first type, e.g. make
202
- // "typedef int Foo, *Foo_p;" -> "using Foo = int;\nusing Foo_p = Foo*;"
203
- if (Type == FirstTypedefType && !QualifierStr.empty ())
204
- Type = FirstTypedefName;
205
- }
206
-
207
- if (!ReplaceRange.getEnd ().isMacroID ()) {
208
- const SourceLocation::IntTy Offset =
209
- MatchedDecl->getFunctionType () ? 0 : Name.size ();
210
- LastReplacementEnd = ReplaceRange.getEnd ().getLocWithOffset (Offset);
93
+ diag (LastCommaOrSemi, UseUsingWarning)
94
+ << FixItHint::CreateReplacement (
95
+ LastCommaOrSemi,
96
+ (" ;\n using " + MatchedDecl.getName () + " = " + FirstTypedefName)
97
+ .str ())
98
+ << FixItHint::CreateRemoval (RemovalRange);
211
99
}
212
100
213
- auto Diag = diag (ReplaceRange.getBegin (), UseUsingWarning);
214
-
215
- // If typedef contains a full tag declaration, extract its full text.
216
- auto LastTagDeclRange = LastTagDeclRanges.find (ParentDecl);
217
- if (LastTagDeclRange != LastTagDeclRanges.end () &&
218
- LastTagDeclRange->second .isValid () &&
219
- ReplaceRange.fullyContains (LastTagDeclRange->second )) {
220
- Type = std::string (Lexer::getSourceText (
221
- CharSourceRange::getTokenRange (LastTagDeclRange->second ), SM, LO));
222
- if (Type.empty ())
223
- return ;
224
- }
225
-
226
- std::string Replacement = (Using + Name + " = " + Type + QualifierStr).str ();
227
- Diag << FixItHint::CreateReplacement (ReplaceRange, Replacement);
101
+ const Token CommaOrSemi = *Lexer::findNextToken (
102
+ MatchedDecl.getEndLoc (), SM, LO, /* IncludeComments=*/ false );
103
+ NextTypedefStartsANewSequence = CommaOrSemi.isNot (tok::TokenKind::comma);
104
+ LastCommaOrSemi = CommaOrSemi.getLocation ();
228
105
}
106
+
229
107
} // namespace clang::tidy::modernize
0 commit comments