diff options
author | Shuai Wang <shuaiwang@google.com> | 2018-08-03 17:23:37 +0000 |
---|---|---|
committer | Shuai Wang <shuaiwang@google.com> | 2018-08-03 17:23:37 +0000 |
commit | b318ad0396e10edd9324d8f127471e3f526eb021 (patch) | |
tree | 347df21411a9a84e2191bf8e0cea07adc7d09ab3 /clang-tidy/performance | |
parent | bd4816fe3b99b87907f1fe1ad4ef520e95f29504 (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.cpp | 73 |
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 " |