diff --git a/clang-tools-extra/clang-tidy/utils/DeclRefExprUtils.cpp b/clang-tools-extra/clang-tidy/utils/DeclRefExprUtils.cpp --- a/clang-tools-extra/clang-tidy/utils/DeclRefExprUtils.cpp +++ b/clang-tools-extra/clang-tidy/utils/DeclRefExprUtils.cpp @@ -46,14 +46,19 @@ ASTContext &Context) { auto DeclRefToVar = declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef"); + auto MemberExprOfVar = memberExpr(has(DeclRefToVar)); + auto DeclRefToVarOrMemberExprOfVar = + stmt(anyOf(DeclRefToVar, MemberExprOfVar)); auto ConstMethodCallee = callee(cxxMethodDecl(isConst())); // Match method call expressions where the variable is referenced as the this // implicit object argument and opertor call expression for member operators // where the variable is the 0-th argument. auto Matches = match( - findAll(expr(anyOf(cxxMemberCallExpr(ConstMethodCallee, on(DeclRefToVar)), - cxxOperatorCallExpr(ConstMethodCallee, - hasArgument(0, DeclRefToVar))))), + findAll(expr(anyOf( + cxxMemberCallExpr(ConstMethodCallee, + on(DeclRefToVarOrMemberExprOfVar)), + cxxOperatorCallExpr(ConstMethodCallee, + hasArgument(0, DeclRefToVarOrMemberExprOfVar))))), Stmt, Context); SmallPtrSet DeclRefs; extractNodesByIdTo(Matches, "declRef", DeclRefs); @@ -65,22 +70,23 @@ ConstReferenceOrValue, substTemplateTypeParmType(hasReplacementType(ConstReferenceOrValue)))); auto UsedAsConstRefOrValueArg = forEachArgumentWithParam( - DeclRefToVar, parmVarDecl(hasType(ConstReferenceOrValueOrReplaced))); + DeclRefToVarOrMemberExprOfVar, + parmVarDecl(hasType(ConstReferenceOrValueOrReplaced))); Matches = match(findAll(invocation(UsedAsConstRefOrValueArg)), Stmt, Context); extractNodesByIdTo(Matches, "declRef", DeclRefs); // References and pointers to const assignments. - Matches = - match(findAll(declStmt( - has(varDecl(hasType(qualType(matchers::isReferenceToConst())), - hasInitializer(ignoringImpCasts(DeclRefToVar)))))), - Stmt, Context); + Matches = match( + findAll(declStmt(has(varDecl( + hasType(qualType(matchers::isReferenceToConst())), + hasInitializer(ignoringImpCasts(DeclRefToVarOrMemberExprOfVar)))))), + Stmt, Context); extractNodesByIdTo(Matches, "declRef", DeclRefs); - Matches = - match(findAll(declStmt(has(varDecl( - hasType(qualType(matchers::isPointerToConst())), - hasInitializer(ignoringImpCasts(unaryOperator( - hasOperatorName("&"), hasUnaryOperand(DeclRefToVar)))))))), - Stmt, Context); + Matches = match(findAll(declStmt(has(varDecl( + hasType(qualType(matchers::isPointerToConst())), + hasInitializer(ignoringImpCasts(unaryOperator( + hasOperatorName("&"), + hasUnaryOperand(DeclRefToVarOrMemberExprOfVar)))))))), + Stmt, Context); extractNodesByIdTo(Matches, "declRef", DeclRefs); return DeclRefs; } diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance-for-range-copy.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance-for-range-copy.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/performance-for-range-copy.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/performance-for-range-copy.cpp @@ -296,3 +296,37 @@ // SS : createView(*ValueReturningIterator())) { } } + +void positiveConstMemberExpr() { + struct Struct { + Mutable Member; + }; + for (Struct SS : View>()) { + // CHECK-MESSAGES: [[@LINE-1]]:15: warning: loop variable is copied + // CHECK-FIXES: for (const Struct& SS : View>()) { + auto MemberCopy = SS.Member; + const auto &ConstRef = SS.Member; + bool b = SS.Member.constMethod(); + use(SS.Member); + useByConstValue(SS.Member); + useByValue(SS.Member); + } +} + +void negativeNonConstMemberExpr() { + struct Struct { + Mutable Member; + }; + for (Struct SS : View>()) { + SS.Member.setBool(true); + } + for (Struct SS : View>()) { + SS.Member[1]; + } + for (Struct SS : View>()) { + mutate(SS.Member); + } + for (Struct SS : View>()) { + mutate(&SS.Member); + } +}