aboutsummaryrefslogtreecommitdiff
path: root/clang-tidy/performance
diff options
context:
space:
mode:
authorShuai Wang <shuaiwang@google.com>2018-08-03 17:23:37 +0000
committerShuai Wang <shuaiwang@google.com>2018-08-03 17:23:37 +0000
commitb318ad0396e10edd9324d8f127471e3f526eb021 (patch)
tree347df21411a9a84e2191bf8e0cea07adc7d09ab3 /clang-tidy/performance
parentbd4816fe3b99b87907f1fe1ad4ef520e95f29504 (diff)
Use ExprMutationAnalyzer in performance-unnecessary-value-param
Summary: This yields better recall as ExprMutationAnalyzer is more accurate. One common pattern this check is now able to catch is: ``` void foo(std::vector<X> v) { for (const auto& elm : v) { // ... } } ``` Reviewers: george.karpenkov Subscribers: a.sidorin, cfe-commits Differential Revision: https://reviews.llvm.org/D50102 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@338903 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'clang-tidy/performance')
-rw-r--r--clang-tidy/performance/UnnecessaryValueParamCheck.cpp73
1 files changed, 39 insertions, 34 deletions
diff --git a/clang-tidy/performance/UnnecessaryValueParamCheck.cpp b/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
index e277ad34..588a2982 100644
--- a/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
+++ b/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
@@ -10,6 +10,7 @@
#include "UnnecessaryValueParamCheck.h"
#include "../utils/DeclRefExprUtils.h"
+#include "../utils/ExprMutationAnalyzer.h"
#include "../utils/FixItHintUtils.h"
#include "../utils/Matchers.h"
#include "../utils/TypeTraits.h"
@@ -31,14 +32,6 @@ std::string paramNameOrIndex(StringRef Name, size_t Index) {
.str();
}
-template <typename S>
-bool isSubset(const S &SubsetCandidate, const S &SupersetCandidate) {
- for (const auto &E : SubsetCandidate)
- if (SupersetCandidate.count(E) == 0)
- return false;
- return true;
-}
-
bool isReferencedOutsideOfCallExpr(const FunctionDecl &Function,
ASTContext &Context) {
auto Matches = match(declRefExpr(to(functionDecl(equalsNode(&Function))),
@@ -98,43 +91,55 @@ void UnnecessaryValueParamCheck::registerMatchers(MatchFinder *Finder) {
void UnnecessaryValueParamCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param");
const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("functionDecl");
- const size_t Index = std::find(Function->parameters().begin(),
- Function->parameters().end(), Param) -
- Function->parameters().begin();
- bool IsConstQualified =
- Param->getType().getCanonicalType().isConstQualified();
- auto AllDeclRefExprs = utils::decl_ref_expr::allDeclRefExprs(
- *Param, *Function, *Result.Context);
- auto ConstDeclRefExprs = utils::decl_ref_expr::constReferenceDeclRefExprs(
- *Param, *Function, *Result.Context);
-
- // Do not trigger on non-const value parameters when they are not only used as
- // const.
- if (!isSubset(AllDeclRefExprs, ConstDeclRefExprs))
+ // Do not trigger on non-const value parameters when they are mutated either
+ // within the function body or within init expression(s) when the function is
+ // a ctor.
+ if (utils::ExprMutationAnalyzer(Function->getBody(), Result.Context)
+ .isMutated(Param))
return;
+ // CXXCtorInitializer might also mutate Param but they're not part of function
+ // body, so check them separately here.
+ if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Function)) {
+ for (const auto *Init : Ctor->inits()) {
+ if (utils::ExprMutationAnalyzer(Init->getInit(), Result.Context)
+ .isMutated(Param))
+ return;
+ }
+ }
+
+ const bool IsConstQualified =
+ Param->getType().getCanonicalType().isConstQualified();
// If the parameter is non-const, check if it has a move constructor and is
// only referenced once to copy-construct another object or whether it has a
// move assignment operator and is only referenced once when copy-assigned.
// In this case wrap DeclRefExpr with std::move() to avoid the unnecessary
// copy.
- if (!IsConstQualified && AllDeclRefExprs.size() == 1) {
- auto CanonicalType = Param->getType().getCanonicalType();
- const auto &DeclRefExpr = **AllDeclRefExprs.begin();
-
- if (!hasLoopStmtAncestor(DeclRefExpr, *Function, *Result.Context) &&
- ((utils::type_traits::hasNonTrivialMoveConstructor(CanonicalType) &&
- utils::decl_ref_expr::isCopyConstructorArgument(
- DeclRefExpr, *Function, *Result.Context)) ||
- (utils::type_traits::hasNonTrivialMoveAssignment(CanonicalType) &&
- utils::decl_ref_expr::isCopyAssignmentArgument(
- DeclRefExpr, *Function, *Result.Context)))) {
- handleMoveFix(*Param, DeclRefExpr, *Result.Context);
- return;
+ if (!IsConstQualified) {
+ auto AllDeclRefExprs = utils::decl_ref_expr::allDeclRefExprs(
+ *Param, *Function, *Result.Context);
+ if (AllDeclRefExprs.size() == 1) {
+ auto CanonicalType = Param->getType().getCanonicalType();
+ const auto &DeclRefExpr = **AllDeclRefExprs.begin();
+
+ if (!hasLoopStmtAncestor(DeclRefExpr, *Function, *Result.Context) &&
+ ((utils::type_traits::hasNonTrivialMoveConstructor(CanonicalType) &&
+ utils::decl_ref_expr::isCopyConstructorArgument(
+ DeclRefExpr, *Function, *Result.Context)) ||
+ (utils::type_traits::hasNonTrivialMoveAssignment(CanonicalType) &&
+ utils::decl_ref_expr::isCopyAssignmentArgument(
+ DeclRefExpr, *Function, *Result.Context)))) {
+ handleMoveFix(*Param, DeclRefExpr, *Result.Context);
+ return;
+ }
}
}
+ const size_t Index = std::find(Function->parameters().begin(),
+ Function->parameters().end(), Param) -
+ Function->parameters().begin();
+
auto Diag =
diag(Param->getLocation(),
IsConstQualified ? "the const qualified parameter %0 is "