Skip to content

Commit 02656bd

Browse files
[clang-tidy] Enhance the check for unnecessary copy initialization in the scenario of MemberExpr initialization.
1 parent f1ca88c commit 02656bd

File tree

3 files changed

+61
-1
lines changed

3 files changed

+61
-1
lines changed

clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,15 @@ void UnnecessaryCopyInitialization::registerMatchers(MatchFinder *Finder) {
273273
Finder->addMatcher(LocalVarCopiedFrom(declRefExpr(
274274
to(varDecl(hasLocalStorage()).bind(OldVarDeclId)))),
275275
this);
276+
277+
Finder->addMatcher(
278+
LocalVarCopiedFrom(memberExpr(
279+
hasObjectExpression(expr(ignoringParenImpCasts(declRefExpr(
280+
to(varDecl(anyOf(hasType(isConstQualified()),
281+
hasType(references(isConstQualified()))))
282+
.bind(OldVarDeclId)))))),
283+
member(fieldDecl().bind("fieldDecl")))),
284+
this);
276285
}
277286

278287
void UnnecessaryCopyInitialization::check(
@@ -294,6 +303,7 @@ void UnnecessaryCopyInitialization::check(
294303
IssueFix, IsVarUnused, IsVarOnlyUsedAsConst};
295304
const auto *OldVar = Result.Nodes.getNodeAs<VarDecl>(OldVarDeclId);
296305
const auto *ObjectArg = Result.Nodes.getNodeAs<VarDecl>(ObjectArgId);
306+
const auto *FD = Result.Nodes.getNodeAs<FieldDecl>("fieldDecl");
297307
const auto *CtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>("ctorCall");
298308

299309
TraversalKindScope RAII(*Result.Context, TK_AsIs);
@@ -318,9 +328,12 @@ void UnnecessaryCopyInitialization::check(
318328
if (OldVar == nullptr) {
319329
// `auto NewVar = functionCall();`
320330
handleCopyFromMethodReturn(Context, ObjectArg);
321-
} else {
331+
} else if (FD == nullptr){
322332
// `auto NewVar = OldVar;`
323333
handleCopyFromLocalVar(Context, *OldVar);
334+
} else {
335+
// `auto NewVar = OldVar.FD;`
336+
handleCopyFromConstLocalVarMember(Context, *OldVar);
324337
}
325338
}
326339

@@ -345,6 +358,11 @@ void UnnecessaryCopyInitialization::handleCopyFromLocalVar(
345358
diagnoseCopyFromLocalVar(Ctx, OldVar);
346359
}
347360

361+
void UnnecessaryCopyInitialization::handleCopyFromConstLocalVarMember(
362+
const CheckContext &Ctx, const VarDecl &OldVar) {
363+
diagnoseCopyFromConstLocalVarMember(Ctx, OldVar);
364+
}
365+
348366
void UnnecessaryCopyInitialization::diagnoseCopyFromMethodReturn(
349367
const CheckContext &Ctx) {
350368
auto Diagnostic =
@@ -369,6 +387,18 @@ void UnnecessaryCopyInitialization::diagnoseCopyFromLocalVar(
369387
maybeIssueFixes(Ctx, Diagnostic);
370388
}
371389

390+
void UnnecessaryCopyInitialization::diagnoseCopyFromConstLocalVarMember(
391+
const CheckContext &Ctx, const VarDecl &OldVar) {
392+
auto Diagnostic =
393+
diag(Ctx.Var.getLocation(),
394+
"local copy %1 of the field of the variable %0 is never "
395+
"modified%select{"
396+
"| and never used}2; consider %select{avoiding the copy|removing "
397+
"the statement}2")
398+
<< &OldVar << &Ctx.Var << Ctx.IsVarUnused;
399+
maybeIssueFixes(Ctx, Diagnostic);
400+
}
401+
372402
void UnnecessaryCopyInitialization::maybeIssueFixes(
373403
const CheckContext &Ctx, DiagnosticBuilder &Diagnostic) {
374404
if (Ctx.IssueFix) {

clang-tools-extra/clang-tidy/performance/UnnecessaryCopyInitialization.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,17 @@ class UnnecessaryCopyInitialization : public ClangTidyCheck {
5050
virtual void diagnoseCopyFromMethodReturn(const CheckContext &Ctx);
5151
virtual void diagnoseCopyFromLocalVar(const CheckContext &Ctx,
5252
const VarDecl &OldVar);
53+
virtual void diagnoseCopyFromConstLocalVarMember(const CheckContext &Ctx,
54+
const VarDecl &OldVar);
5355

5456
private:
5557
void handleCopyFromMethodReturn(const CheckContext &Ctx,
5658
const VarDecl *ObjectArg);
5759
void handleCopyFromLocalVar(const CheckContext &Ctx, const VarDecl &OldVar);
5860

61+
void handleCopyFromConstLocalVarMember(const CheckContext &Ctx,
62+
const VarDecl &OldVar);
63+
5964
void maybeIssueFixes(const CheckContext &Ctx, DiagnosticBuilder &Diagnostic);
6065

6166
const std::vector<StringRef> AllowedTypes;

clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-copy-initialization.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,3 +939,28 @@ template<typename T> bool OperatorWithNoDirectCallee(T t) {
939939
return a1 == t;
940940
}
941941

942+
bool CopiedFromConstRefParmVar(const Struct &crs, const Struct cs, Struct &rs, Struct s) {
943+
const auto m1 = crs.Member;
944+
// CHECK-MESSAGES: [[@LINE-1]]:14: warning: local copy 'm1' of the field of the variable 'crs' is never modified; consider avoiding the copy
945+
// CHECK-FIXES: const auto& m1 = crs.Member;
946+
const auto m2 = cs.Member;
947+
// CHECK-MESSAGES: [[@LINE-1]]:14: warning: local copy 'm2' of the field of the variable 'cs' is never modified; consider avoiding the copy
948+
// CHECK-FIXES: const auto& m2 = cs.Member;
949+
const auto m3 = rs.Member;
950+
const auto m4 = s.Member;
951+
return m1 == m2 || m3 == m4;
952+
}
953+
954+
const Struct GlobalS;
955+
bool CopiedFromConstLocalVar() {
956+
const Struct crs;
957+
Struct s;
958+
const auto m1 = crs.Member;
959+
// CHECK-MESSAGES: [[@LINE-1]]:14: warning: local copy 'm1' of the field of the variable 'crs' is never modified; consider avoiding the copy
960+
// CHECK-FIXES: const auto& m1 = crs.Member;
961+
const auto m2 = s.Member;
962+
const auto m3 = GlobalS.Member;
963+
// CHECK-MESSAGES: [[@LINE-1]]:14: warning: local copy 'm3' of the field of the variable 'GlobalS' is never modified; consider avoiding the copy
964+
// CHECK-FIXES: const auto& m3 = GlobalS.Member;
965+
return m1 == m2 || m2 == m3;
966+
}

0 commit comments

Comments
 (0)