15
15
#include " clang/AST/Decl.h"
16
16
#include " clang/Basic/Diagnostic.h"
17
17
#include < optional>
18
+ #include < utility>
18
19
19
20
namespace clang ::tidy::performance {
20
21
namespace {
@@ -263,19 +264,25 @@ void UnnecessaryCopyInitialization::registerMatchers(MatchFinder *Finder) {
263
264
264
265
void UnnecessaryCopyInitialization::check (
265
266
const MatchFinder::MatchResult &Result) {
266
- const auto *NewVar = Result.Nodes .getNodeAs <VarDecl>(" newVarDecl" );
267
+ const auto &NewVar = *Result.Nodes .getNodeAs <VarDecl>(" newVarDecl" );
268
+ const auto &BlockStmt = *Result.Nodes .getNodeAs <Stmt>(" blockStmt" );
269
+ const auto &VarDeclStmt = *Result.Nodes .getNodeAs <DeclStmt>(" declStmt" );
270
+ // Do not propose fixes if the DeclStmt has multiple VarDecls or in
271
+ // macros since we cannot place them correctly.
272
+ const bool IssueFix =
273
+ VarDeclStmt.isSingleDecl () && !NewVar.getLocation ().isMacroID ();
274
+ const bool IsVarUnused = isVariableUnused (NewVar, BlockStmt, *Result.Context );
275
+ const bool IsVarOnlyUsedAsConst =
276
+ isOnlyUsedAsConst (NewVar, BlockStmt, *Result.Context );
277
+ const CheckContext Context{
278
+ NewVar, BlockStmt, VarDeclStmt, *Result.Context ,
279
+ IssueFix, IsVarUnused, IsVarOnlyUsedAsConst};
267
280
const auto *OldVar = Result.Nodes .getNodeAs <VarDecl>(OldVarDeclId);
268
281
const auto *ObjectArg = Result.Nodes .getNodeAs <VarDecl>(ObjectArgId);
269
- const auto *BlockStmt = Result.Nodes .getNodeAs <Stmt>(" blockStmt" );
270
282
const auto *CtorCall = Result.Nodes .getNodeAs <CXXConstructExpr>(" ctorCall" );
271
- const auto *Stmt = Result.Nodes .getNodeAs <DeclStmt>(" declStmt" );
272
283
273
284
TraversalKindScope RAII (*Result.Context , TK_AsIs);
274
285
275
- // Do not propose fixes if the DeclStmt has multiple VarDecls or in macros
276
- // since we cannot place them correctly.
277
- bool IssueFix = Stmt->isSingleDecl () && !NewVar->getLocation ().isMacroID ();
278
-
279
286
// A constructor that looks like T(const T& t, bool arg = false) counts as a
280
287
// copy only when it is called with default arguments for the arguments after
281
288
// the first.
@@ -289,74 +296,71 @@ void UnnecessaryCopyInitialization::check(
289
296
// instantiations where the types differ and rely on implicit conversion would
290
297
// no longer compile if we switched to a reference.
291
298
if (differentReplacedTemplateParams (
292
- NewVar-> getType (), constructorArgumentType (OldVar, Result.Nodes ),
299
+ Context. Var . getType (), constructorArgumentType (OldVar, Result.Nodes ),
293
300
*Result.Context ))
294
301
return ;
295
302
296
303
if (OldVar == nullptr ) {
297
- handleCopyFromMethodReturn (*NewVar, *BlockStmt, *Stmt, IssueFix, ObjectArg,
298
- *Result. Context );
304
+ // `auto NewVar = functionCall();`
305
+ handleCopyFromMethodReturn ( Context, ObjectArg );
299
306
} else {
300
- handleCopyFromLocalVar (*NewVar, *OldVar, *BlockStmt, *Stmt, IssueFix,
301
- *Result. Context );
307
+ // `auto NewVar = OldVar;`
308
+ handleCopyFromLocalVar (Context, *OldVar );
302
309
}
303
310
}
304
311
305
312
void UnnecessaryCopyInitialization::handleCopyFromMethodReturn (
306
- const VarDecl &Var, const Stmt &BlockStmt, const DeclStmt &Stmt,
307
- bool IssueFix, const VarDecl *ObjectArg, ASTContext &Context) {
308
- bool IsConstQualified = Var.getType ().isConstQualified ();
309
- if (!IsConstQualified && !isOnlyUsedAsConst (Var, BlockStmt, Context))
313
+ const CheckContext &Ctx, const VarDecl *ObjectArg) {
314
+ bool IsConstQualified = Ctx.Var .getType ().isConstQualified ();
315
+ if (!IsConstQualified && !Ctx.IsVarOnlyUsedAsConst )
310
316
return ;
311
317
if (ObjectArg != nullptr &&
312
- !isInitializingVariableImmutable (*ObjectArg, BlockStmt, Context ,
318
+ !isInitializingVariableImmutable (*ObjectArg, Ctx. BlockStmt , Ctx. ASTCtx ,
313
319
ExcludedContainerTypes))
314
320
return ;
315
- if (isVariableUnused (Var, BlockStmt, Context)) {
316
- auto Diagnostic =
317
- diag (Var.getLocation (),
318
- " the %select{|const qualified }0variable %1 is copy-constructed "
319
- " from a const reference but is never used; consider "
320
- " removing the statement" )
321
- << IsConstQualified << &Var;
322
- if (IssueFix)
323
- recordRemoval (Stmt, Context, Diagnostic);
324
- } else {
325
- auto Diagnostic =
326
- diag (Var.getLocation (),
327
- " the %select{|const qualified }0variable %1 is copy-constructed "
328
- " from a const reference%select{ but is only used as const "
329
- " reference|}0; consider making it a const reference" )
330
- << IsConstQualified << &Var;
331
- if (IssueFix)
332
- recordFixes (Var, Context, Diagnostic);
333
- }
321
+ diagnoseCopyFromMethodReturn (Ctx);
334
322
}
335
323
336
324
void UnnecessaryCopyInitialization::handleCopyFromLocalVar (
337
- const VarDecl &NewVar, const VarDecl &OldVar, const Stmt &BlockStmt,
338
- const DeclStmt &Stmt, bool IssueFix, ASTContext &Context) {
339
- if (!isOnlyUsedAsConst (NewVar, BlockStmt, Context) ||
340
- !isInitializingVariableImmutable (OldVar, BlockStmt, Context,
325
+ const CheckContext &Ctx, const VarDecl &OldVar) {
326
+ if (!Ctx.IsVarOnlyUsedAsConst ||
327
+ !isInitializingVariableImmutable (OldVar, Ctx.BlockStmt , Ctx.ASTCtx ,
341
328
ExcludedContainerTypes))
342
329
return ;
330
+ diagnoseCopyFromLocalVar (Ctx, OldVar);
331
+ }
343
332
344
- if (isVariableUnused (NewVar, BlockStmt, Context)) {
345
- auto Diagnostic = diag (NewVar.getLocation (),
346
- " local copy %0 of the variable %1 is never modified "
347
- " and never used; "
348
- " consider removing the statement" )
349
- << &NewVar << &OldVar;
350
- if (IssueFix)
351
- recordRemoval (Stmt, Context, Diagnostic);
352
- } else {
353
- auto Diagnostic =
354
- diag (NewVar.getLocation (),
355
- " local copy %0 of the variable %1 is never modified; "
356
- " consider avoiding the copy" )
357
- << &NewVar << &OldVar;
358
- if (IssueFix)
359
- recordFixes (NewVar, Context, Diagnostic);
333
+ void UnnecessaryCopyInitialization::diagnoseCopyFromMethodReturn (
334
+ const CheckContext &Ctx) {
335
+ auto Diagnostic =
336
+ diag (Ctx.Var .getLocation (),
337
+ " the %select{|const qualified }0variable %1 is "
338
+ " copy-constructed "
339
+ " from a const reference%select{%select{ but is only used as const "
340
+ " reference|}0| but is never used}2; consider "
341
+ " %select{making it a const reference|removing the statement}2" )
342
+ << Ctx.Var .getType ().isConstQualified () << &Ctx.Var << Ctx.IsVarUnused ;
343
+ maybeIssueFixes (Ctx, Diagnostic);
344
+ }
345
+
346
+ void UnnecessaryCopyInitialization::diagnoseCopyFromLocalVar (
347
+ const CheckContext &Ctx, const VarDecl &OldVar) {
348
+ auto Diagnostic =
349
+ diag (Ctx.Var .getLocation (),
350
+ " local copy %1 of the variable %0 is never modified%select{"
351
+ " | and never used}2; consider %select{avoiding the copy|removing "
352
+ " the statement}2" )
353
+ << &OldVar << &Ctx.Var << Ctx.IsVarUnused ;
354
+ maybeIssueFixes (Ctx, Diagnostic);
355
+ }
356
+
357
+ void UnnecessaryCopyInitialization::maybeIssueFixes (
358
+ const CheckContext &Ctx, DiagnosticBuilder &Diagnostic) {
359
+ if (Ctx.IssueFix ) {
360
+ if (Ctx.IsVarUnused )
361
+ recordRemoval (Ctx.VarDeclStmt , Ctx.ASTCtx , Diagnostic);
362
+ else
363
+ recordFixes (Ctx.Var , Ctx.ASTCtx , Diagnostic);
360
364
}
361
365
}
362
366
0 commit comments