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