@@ -21,7 +21,7 @@ struct FindArgsResult {
21
21
const Expr *First;
22
22
const Expr *Last;
23
23
const Expr *Compare;
24
- std::vector <const clang::Expr *> Args;
24
+ SmallVector <const clang::Expr *, 2 > Args;
25
25
};
26
26
27
27
} // anonymous namespace
@@ -36,30 +36,35 @@ static FindArgsResult findArgs(const CallExpr *Call) {
36
36
Result.Last = nullptr ;
37
37
Result.Compare = nullptr ;
38
38
39
- if (Call->getNumArgs () == 3 ) {
40
- auto ArgIterator = Call->arguments ().begin ();
41
- std::advance (ArgIterator, 2 );
42
- Result.Compare = *ArgIterator;
43
- } else {
39
+ // check if the function has initializer list argument
40
+ if (Call->getNumArgs () < 3 ) {
44
41
auto ArgIterator = Call->arguments ().begin ();
45
42
46
- if (const auto *InitListExpr =
47
- dyn_cast<CXXStdInitializerListExpr>(*ArgIterator)) {
48
- if (const auto *InitList = dyn_cast<clang::InitListExpr>(
49
- InitListExpr->getSubExpr ()->IgnoreImplicit ())) {
50
- Result.Args .insert (Result.Args .begin (), InitList->inits ().begin (),
51
- InitList->inits ().end ());
52
-
53
- Result.First = *ArgIterator;
54
- Result.Last = *ArgIterator;
55
-
56
- std::advance (ArgIterator, 1 );
57
- if (ArgIterator != Call->arguments ().end ()) {
58
- Result.Compare = *ArgIterator;
59
- }
60
- return Result;
43
+ const auto *InitListExpr =
44
+ dyn_cast<CXXStdInitializerListExpr>(*ArgIterator);
45
+ const auto *InitList =
46
+ InitListExpr != nullptr
47
+ ? dyn_cast<clang::InitListExpr>(
48
+ InitListExpr->getSubExpr ()->IgnoreImplicit ())
49
+ : nullptr ;
50
+
51
+ if (InitListExpr && InitList) {
52
+ Result.Args .insert (Result.Args .begin (), InitList->inits ().begin (),
53
+ InitList->inits ().end ());
54
+ Result.First = *ArgIterator;
55
+ Result.Last = *ArgIterator;
56
+
57
+ // check if there is a comparison argument
58
+ std::advance (ArgIterator, 1 );
59
+ if (ArgIterator != Call->arguments ().end ()) {
60
+ Result.Compare = *ArgIterator;
61
61
}
62
+
63
+ return Result;
62
64
}
65
+ // if it has 3 arguments then the last will be the comparison
66
+ } else {
67
+ Result.Compare = *(std::next (Call->arguments ().begin (), 2 ));
63
68
}
64
69
65
70
for (const Expr *Arg : Call->arguments ()) {
@@ -76,114 +81,133 @@ static FindArgsResult findArgs(const CallExpr *Call) {
76
81
return Result;
77
82
}
78
83
79
- static std::vector <FixItHint>
84
+ static SmallVector <FixItHint>
80
85
generateReplacement (const MatchFinder::MatchResult &Match,
81
86
const CallExpr *TopCall, const FindArgsResult &Result) {
82
- std::vector <FixItHint> FixItHints;
87
+ SmallVector <FixItHint> FixItHints;
83
88
84
89
const QualType ResultType = TopCall->getDirectCallee ()
85
90
->getReturnType ()
86
91
.getNonReferenceType ()
87
92
.getUnqualifiedType ()
88
93
.getCanonicalType ();
94
+ const auto &SourceMngr = *Match.SourceManager ;
95
+ const auto LanguageOpts = Match.Context ->getLangOpts ();
89
96
const bool IsInitializerList = Result.First == Result.Last ;
90
97
91
- if (!IsInitializerList)
98
+ // add { and } if the top call doesn't have an initializer list arg
99
+ if (!IsInitializerList) {
92
100
FixItHints.push_back (
93
101
FixItHint::CreateInsertion (Result.First ->getBeginLoc (), " {" ));
94
102
103
+ if (Result.Compare )
104
+ FixItHints.push_back (FixItHint::CreateInsertion (
105
+ Lexer::getLocForEndOfToken (Result.Last ->getEndLoc (), 0 , SourceMngr,
106
+ LanguageOpts),
107
+ " }" ));
108
+ else
109
+ FixItHints.push_back (
110
+ FixItHint::CreateInsertion (TopCall->getEndLoc (), " }" ));
111
+ }
112
+
95
113
for (const Expr *Arg : Result.Args ) {
96
- if (const auto *InnerCall =
97
- dyn_cast<CallExpr>(Arg->IgnoreParenImpCasts ())) {
98
- const FindArgsResult InnerResult = findArgs (InnerCall);
99
- const std::vector<FixItHint> InnerReplacements =
100
- generateReplacement (Match, InnerCall, InnerResult);
101
- if (InnerCall->getDirectCallee ()->getQualifiedNameAsString () ==
102
- TopCall->getDirectCallee ()->getQualifiedNameAsString () &&
103
- ((!Result.Compare && !InnerResult.Compare ) ||
104
- utils::areStatementsIdentical (Result.Compare , InnerResult.Compare ,
105
- *Match.Context ))) {
114
+ const auto *InnerCall = dyn_cast<CallExpr>(Arg->IgnoreParenImpCasts ());
106
115
107
- FixItHints.push_back (
108
- FixItHint::CreateRemoval (CharSourceRange::getTokenRange (
109
- InnerCall->getCallee ()->getSourceRange ())));
116
+ // If the argument is not a nested call
117
+ if (!InnerCall) {
118
+ // check if typecast is required
119
+ const QualType ArgType = Arg->IgnoreParenImpCasts ()
120
+ ->getType ()
121
+ .getUnqualifiedType ()
122
+ .getCanonicalType ();
110
123
111
- const auto LParen = utils::lexer::findNextTokenSkippingComments (
112
- InnerCall->getCallee ()->getEndLoc (), *Match.SourceManager ,
113
- Match.Context ->getLangOpts ());
114
- if (LParen && LParen->getKind () == tok::l_paren)
115
- FixItHints.push_back (
116
- FixItHint::CreateRemoval (SourceRange (LParen->getLocation ())));
124
+ if (ArgType == ResultType)
125
+ continue ;
126
+
127
+ const std::string ArgText =
128
+ Lexer::getSourceText (
129
+ CharSourceRange::getTokenRange (Arg->getSourceRange ()), SourceMngr,
130
+ LanguageOpts)
131
+ .str ();
132
+
133
+ Twine Replacement = llvm::Twine (" static_cast<" )
134
+ .concat (ResultType.getAsString (LanguageOpts))
135
+ .concat (" >(" )
136
+ .concat (ArgText)
137
+ .concat (" )" );
138
+
139
+ FixItHints.push_back (FixItHint::CreateReplacement (Arg->getSourceRange (),
140
+ Replacement.str ()));
117
141
118
- FixItHints.push_back (
119
- FixItHint::CreateRemoval (SourceRange (InnerCall->getRParenLoc ())));
120
-
121
- if (InnerResult.First == InnerResult.Last ) {
122
- FixItHints.insert (FixItHints.end (), InnerReplacements.begin (),
123
- InnerReplacements.end ());
124
-
125
- FixItHints.push_back (
126
- FixItHint::CreateRemoval (CharSourceRange::getTokenRange (
127
- InnerResult.First ->getBeginLoc ())));
128
- FixItHints.push_back (FixItHint::CreateRemoval (
129
- CharSourceRange::getTokenRange (InnerResult.First ->getEndLoc ())));
130
- } else
131
- FixItHints.insert (FixItHints.end (), InnerReplacements.begin () + 1 ,
132
- InnerReplacements.end () - 1 );
133
-
134
- if (InnerResult.Compare ) {
135
- const auto Comma = utils::lexer::findNextTokenSkippingComments (
136
- InnerResult.Last ->getEndLoc (), *Match.SourceManager ,
137
- Match.Context ->getLangOpts ());
138
- if (Comma && Comma->getKind () == tok::comma)
139
- FixItHints.push_back (
140
- FixItHint::CreateRemoval (SourceRange (Comma->getLocation ())));
141
-
142
- if (utils::lexer::getPreviousToken (
143
- InnerResult.Compare ->getExprLoc (), *Match.SourceManager ,
144
- Match.Context ->getLangOpts (), false )
145
- .getLocation () == Comma->getLocation ())
146
- FixItHints.push_back (
147
- FixItHint::CreateRemoval (CharSourceRange::getTokenRange (
148
- Comma->getLocation (), InnerResult.Compare ->getEndLoc ())));
149
- else {
150
- FixItHints.push_back (
151
- FixItHint::CreateRemoval (CharSourceRange::getTokenRange (
152
- InnerResult.Compare ->getSourceRange ())));
153
- }
154
- }
155
- }
156
142
continue ;
157
143
}
158
144
159
- const QualType ArgType = Arg-> IgnoreParenImpCasts ()
160
- -> getType ()
161
- . getUnqualifiedType ()
162
- . getCanonicalType () ;
145
+ const auto InnerResult = findArgs (InnerCall);
146
+ const auto InnerReplacements =
147
+ generateReplacement (Match, InnerCall, InnerResult);
148
+ const bool IsInnerInitializerList = InnerResult. First == InnerResult. Last ;
163
149
164
- if (ArgType != ResultType) {
165
- const std::string ArgText =
166
- Lexer::getSourceText (
167
- CharSourceRange::getTokenRange (Arg->getSourceRange ()),
168
- *Match.SourceManager , Match.Context ->getLangOpts ())
169
- .str ();
150
+ // if the nested call is not the same as the top call
151
+ if (InnerCall->getDirectCallee ()->getQualifiedNameAsString () !=
152
+ TopCall->getDirectCallee ()->getQualifiedNameAsString ())
153
+ continue ;
154
+
155
+ // if the nested call doesn't have the same compare function
156
+ if ((Result.Compare || InnerResult.Compare ) &&
157
+ !utils::areStatementsIdentical (Result.Compare , InnerResult.Compare ,
158
+ *Match.Context ))
159
+ continue ;
160
+
161
+ // remove the function call
162
+ FixItHints.push_back (
163
+ FixItHint::CreateRemoval (CharSourceRange::getTokenRange (
164
+ InnerCall->getCallee ()->getSourceRange ())));
170
165
171
- FixItHints.push_back (FixItHint::CreateReplacement (
172
- Arg->getSourceRange (),
173
- " static_cast<" + ResultType.getAsString () + " >(" + ArgText + " )" ));
166
+ // remove the parentheses
167
+ const auto LParen = utils::lexer::findNextTokenSkippingComments (
168
+ InnerCall->getCallee ()->getEndLoc (), SourceMngr, LanguageOpts);
169
+ FixItHints.push_back (
170
+ FixItHint::CreateRemoval (SourceRange (LParen->getLocation ())));
171
+ FixItHints.push_back (
172
+ FixItHint::CreateRemoval (SourceRange (InnerCall->getRParenLoc ())));
173
+
174
+ // if the inner call has an initializer list arg
175
+ if (IsInnerInitializerList) {
176
+ // remove the initializer list braces
177
+ FixItHints.push_back (FixItHint::CreateRemoval (
178
+ CharSourceRange::getTokenRange (InnerResult.First ->getBeginLoc ())));
179
+ FixItHints.push_back (FixItHint::CreateRemoval (
180
+ CharSourceRange::getTokenRange (InnerResult.First ->getEndLoc ())));
174
181
}
175
- }
176
182
177
- if (!IsInitializerList) {
178
- if (Result.Compare )
179
- FixItHints.push_back (FixItHint::CreateInsertion (
180
- Lexer::getLocForEndOfToken (Result.Last ->getEndLoc (), 0 ,
181
- *Match.SourceManager ,
182
- Match.Context ->getLangOpts ()),
183
- " }" ));
184
- else
185
- FixItHints.push_back (
186
- FixItHint::CreateInsertion (TopCall->getEndLoc (), " }" ));
183
+ FixItHints.insert (FixItHints.end (),
184
+ // ignore { and } insertions for the inner call if it does
185
+ // not have an initializer list arg
186
+ InnerReplacements.begin () + (!IsInnerInitializerList) * 2 ,
187
+ InnerReplacements.end ());
188
+
189
+ if (InnerResult.Compare ) {
190
+ // find the comma after the value arguments
191
+ const auto Comma = utils::lexer::findNextTokenSkippingComments (
192
+ InnerResult.Last ->getEndLoc (), SourceMngr, LanguageOpts);
193
+
194
+ // if there are comments between the comma and the comparison
195
+ if (utils::lexer::getPreviousToken (InnerResult.Compare ->getExprLoc (),
196
+ SourceMngr, LanguageOpts, false )
197
+ .getLocation () != Comma->getLocation ()) {
198
+ // remove the comma and the comparison
199
+ FixItHints.push_back (
200
+ FixItHint::CreateRemoval (SourceRange (Comma->getLocation ())));
201
+
202
+ FixItHints.push_back (
203
+ FixItHint::CreateRemoval (CharSourceRange::getTokenRange (
204
+ InnerResult.Compare ->getSourceRange ())));
205
+ } else
206
+ // remove everything after the last argument
207
+ FixItHints.push_back (
208
+ FixItHint::CreateRemoval (CharSourceRange::getTokenRange (
209
+ Comma->getLocation (), InnerResult.Compare ->getEndLoc ())));
210
+ }
187
211
}
188
212
189
213
return FixItHints;
@@ -229,7 +253,7 @@ void MinMaxUseInitializerListCheck::check(
229
253
const auto *TopCall = Match.Nodes .getNodeAs <CallExpr>(" topCall" );
230
254
231
255
const FindArgsResult Result = findArgs (TopCall);
232
- const std::vector <FixItHint> Replacement =
256
+ const SmallVector <FixItHint> Replacement =
233
257
generateReplacement (Match, TopCall, Result);
234
258
235
259
if (Replacement.size () <= 2 ) {
0 commit comments