2016-03-29 10:42:38 +08:00
|
|
|
//===--- UnnecessaryValueParamCheck.cpp - clang-tidy-----------------------===//
|
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2016-03-29 10:42:38 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "UnnecessaryValueParamCheck.h"
|
|
|
|
|
|
|
|
#include "../utils/DeclRefExprUtils.h"
|
|
|
|
#include "../utils/FixItHintUtils.h"
|
|
|
|
#include "../utils/Matchers.h"
|
2018-10-12 21:05:21 +08:00
|
|
|
#include "../utils/OptionsUtils.h"
|
2016-07-02 04:12:15 +08:00
|
|
|
#include "../utils/TypeTraits.h"
|
|
|
|
#include "clang/Frontend/CompilerInstance.h"
|
|
|
|
#include "clang/Lex/Lexer.h"
|
|
|
|
#include "clang/Lex/Preprocessor.h"
|
2016-03-29 10:42:38 +08:00
|
|
|
|
|
|
|
using namespace clang::ast_matchers;
|
|
|
|
|
|
|
|
namespace clang {
|
|
|
|
namespace tidy {
|
|
|
|
namespace performance {
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
std::string paramNameOrIndex(StringRef Name, size_t Index) {
|
|
|
|
return (Name.empty() ? llvm::Twine('#') + llvm::Twine(Index + 1)
|
|
|
|
: llvm::Twine('\'') + Name + llvm::Twine('\''))
|
|
|
|
.str();
|
|
|
|
}
|
|
|
|
|
2016-11-10 09:28:22 +08:00
|
|
|
bool isReferencedOutsideOfCallExpr(const FunctionDecl &Function,
|
|
|
|
ASTContext &Context) {
|
|
|
|
auto Matches = match(declRefExpr(to(functionDecl(equalsNode(&Function))),
|
|
|
|
unless(hasAncestor(callExpr()))),
|
|
|
|
Context);
|
|
|
|
return !Matches.empty();
|
|
|
|
}
|
|
|
|
|
2017-01-03 20:10:44 +08:00
|
|
|
bool hasLoopStmtAncestor(const DeclRefExpr &DeclRef, const Decl &Decl,
|
2016-12-16 10:47:56 +08:00
|
|
|
ASTContext &Context) {
|
|
|
|
auto Matches =
|
2017-01-03 20:10:44 +08:00
|
|
|
match(decl(forEachDescendant(declRefExpr(
|
2016-12-16 10:47:56 +08:00
|
|
|
equalsNode(&DeclRef),
|
|
|
|
unless(hasAncestor(stmt(anyOf(forStmt(), cxxForRangeStmt(),
|
2017-01-03 20:10:44 +08:00
|
|
|
whileStmt(), doStmt()))))))),
|
|
|
|
Decl, Context);
|
2016-12-16 10:47:56 +08:00
|
|
|
return Matches.empty();
|
|
|
|
}
|
|
|
|
|
2017-07-26 08:45:41 +08:00
|
|
|
bool isExplicitTemplateSpecialization(const FunctionDecl &Function) {
|
|
|
|
if (const auto *SpecializationInfo = Function.getTemplateSpecializationInfo())
|
|
|
|
if (SpecializationInfo->getTemplateSpecializationKind() ==
|
|
|
|
TSK_ExplicitSpecialization)
|
|
|
|
return true;
|
|
|
|
if (const auto *Method = llvm::dyn_cast<CXXMethodDecl>(&Function))
|
|
|
|
if (Method->getTemplatedKind() == FunctionDecl::TK_MemberSpecialization &&
|
|
|
|
Method->getMemberSpecializationInfo()->isExplicitSpecialization())
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-03-29 10:42:38 +08:00
|
|
|
} // namespace
|
|
|
|
|
2016-07-02 04:12:15 +08:00
|
|
|
UnnecessaryValueParamCheck::UnnecessaryValueParamCheck(
|
|
|
|
StringRef Name, ClangTidyContext *Context)
|
|
|
|
: ClangTidyCheck(Name, Context),
|
|
|
|
IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
|
2018-10-12 21:05:21 +08:00
|
|
|
Options.getLocalOrGlobal("IncludeStyle", "llvm"))),
|
|
|
|
AllowedTypes(
|
|
|
|
utils::options::parseStringList(Options.get("AllowedTypes", ""))) {}
|
2016-07-02 04:12:15 +08:00
|
|
|
|
2016-03-29 10:42:38 +08:00
|
|
|
void UnnecessaryValueParamCheck::registerMatchers(MatchFinder *Finder) {
|
2018-02-02 23:34:33 +08:00
|
|
|
// This check is specific to C++ and doesn't apply to languages like
|
|
|
|
// Objective-C.
|
|
|
|
if (!getLangOpts().CPlusPlus)
|
|
|
|
return;
|
2018-10-12 21:05:21 +08:00
|
|
|
const auto ExpensiveValueParamDecl = parmVarDecl(
|
2018-11-25 10:41:01 +08:00
|
|
|
hasType(qualType(
|
2018-10-12 21:05:21 +08:00
|
|
|
hasCanonicalType(matchers::isExpensiveToCopy()),
|
|
|
|
unless(anyOf(hasCanonicalType(referenceType()),
|
|
|
|
hasDeclaration(namedDecl(
|
2018-11-25 10:41:01 +08:00
|
|
|
matchers::matchesAnyListedName(AllowedTypes))))))),
|
2018-10-12 21:05:21 +08:00
|
|
|
decl().bind("param"));
|
2016-03-29 10:42:38 +08:00
|
|
|
Finder->addMatcher(
|
2017-05-17 01:28:17 +08:00
|
|
|
functionDecl(hasBody(stmt()), isDefinition(), unless(isImplicit()),
|
2016-12-02 22:44:16 +08:00
|
|
|
unless(cxxMethodDecl(anyOf(isOverride(), isFinal()))),
|
2016-03-29 10:42:38 +08:00
|
|
|
has(typeLoc(forEach(ExpensiveValueParamDecl))),
|
2017-05-17 01:28:17 +08:00
|
|
|
unless(isInstantiated()), decl().bind("functionDecl")),
|
2016-03-29 10:42:38 +08:00
|
|
|
this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void UnnecessaryValueParamCheck::check(const MatchFinder::MatchResult &Result) {
|
|
|
|
const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param");
|
|
|
|
const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("functionDecl");
|
|
|
|
|
2018-09-18 01:59:51 +08:00
|
|
|
FunctionParmMutationAnalyzer &Analyzer =
|
|
|
|
MutationAnalyzers.try_emplace(Function, *Function, *Result.Context)
|
|
|
|
.first->second;
|
|
|
|
if (Analyzer.isMutated(Param))
|
2016-07-02 04:12:15 +08:00
|
|
|
return;
|
2018-08-04 01:23:37 +08:00
|
|
|
|
|
|
|
const bool IsConstQualified =
|
|
|
|
Param->getType().getCanonicalType().isConstQualified();
|
2016-07-02 04:12:15 +08:00
|
|
|
|
|
|
|
// 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.
|
2018-08-04 01:23:37 +08:00
|
|
|
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;
|
|
|
|
}
|
2016-07-02 04:12:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-04 01:23:37 +08:00
|
|
|
const size_t Index = std::find(Function->parameters().begin(),
|
|
|
|
Function->parameters().end(), Param) -
|
|
|
|
Function->parameters().begin();
|
|
|
|
|
2016-03-29 10:42:38 +08:00
|
|
|
auto Diag =
|
|
|
|
diag(Param->getLocation(),
|
|
|
|
IsConstQualified ? "the const qualified parameter %0 is "
|
|
|
|
"copied for each invocation; consider "
|
|
|
|
"making it a reference"
|
|
|
|
: "the parameter %0 is copied for each "
|
|
|
|
"invocation but only used as a const reference; "
|
|
|
|
"consider making it a const reference")
|
|
|
|
<< paramNameOrIndex(Param->getName(), Index);
|
2016-11-10 09:28:22 +08:00
|
|
|
// Do not propose fixes when:
|
|
|
|
// 1. the ParmVarDecl is in a macro, since we cannot place them correctly
|
|
|
|
// 2. the function is virtual as it might break overrides
|
|
|
|
// 3. the function is referenced outside of a call expression within the
|
|
|
|
// compilation unit as the signature change could introduce build errors.
|
2017-07-26 08:45:41 +08:00
|
|
|
// 4. the function is an explicit template specialization.
|
2016-07-05 22:40:44 +08:00
|
|
|
const auto *Method = llvm::dyn_cast<CXXMethodDecl>(Function);
|
2018-08-10 06:42:26 +08:00
|
|
|
if (Param->getBeginLoc().isMacroID() || (Method && Method->isVirtual()) ||
|
2017-07-26 08:45:41 +08:00
|
|
|
isReferencedOutsideOfCallExpr(*Function, *Result.Context) ||
|
|
|
|
isExplicitTemplateSpecialization(*Function))
|
2016-03-29 10:42:38 +08:00
|
|
|
return;
|
|
|
|
for (const auto *FunctionDecl = Function; FunctionDecl != nullptr;
|
|
|
|
FunctionDecl = FunctionDecl->getPreviousDecl()) {
|
|
|
|
const auto &CurrentParam = *FunctionDecl->getParamDecl(Index);
|
2016-05-03 10:54:05 +08:00
|
|
|
Diag << utils::fixit::changeVarDeclToReference(CurrentParam,
|
2016-11-08 15:50:19 +08:00
|
|
|
*Result.Context);
|
2016-11-05 04:51:31 +08:00
|
|
|
// The parameter of each declaration needs to be checked individually as to
|
|
|
|
// whether it is const or not as constness can differ between definition and
|
|
|
|
// declaration.
|
[clang-tidy] implement utility-function to add 'const' to variables
Summary:
This patch extends the already existing facility to add 'const' to variables
to be more flexible and correct. The previous version did not consider pointers
as value AND pointee. For future automatic introduction for const-correctness
this shortcoming needs to be fixed.
It always allows configuration where the 'const' token is inserted, either on
the left side (if possible) or the right side.
It adds many unit-tests to the utility-function that did not exist before, as
the function was implicitly tested through clang-tidy checks. These
tests were not changed, as the API is still compatible.
Reviewers: aaron.ballman, hokein, alexfh, shuaiwang, lebedev.ri
Reviewed By: aaron.ballman
Subscribers: jdoerfert, mgorny, xazax.hun, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D54395
2020-01-04 03:36:49 +08:00
|
|
|
if (!CurrentParam.getType().getCanonicalType().isConstQualified()) {
|
|
|
|
if (llvm::Optional<FixItHint> Fix = utils::fixit::addQualifierToVarDecl(
|
|
|
|
CurrentParam, *Result.Context, DeclSpec::TQ::TQ_const))
|
|
|
|
Diag << *Fix;
|
|
|
|
}
|
2016-03-29 10:42:38 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-02 04:12:15 +08:00
|
|
|
void UnnecessaryValueParamCheck::registerPPCallbacks(
|
2019-03-23 02:58:12 +08:00
|
|
|
const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
|
2019-08-15 07:52:23 +08:00
|
|
|
Inserter = std::make_unique<utils::IncludeInserter>(SM, getLangOpts(),
|
[clang-tidy] implement utility-function to add 'const' to variables
Summary:
This patch extends the already existing facility to add 'const' to variables
to be more flexible and correct. The previous version did not consider pointers
as value AND pointee. For future automatic introduction for const-correctness
this shortcoming needs to be fixed.
It always allows configuration where the 'const' token is inserted, either on
the left side (if possible) or the right side.
It adds many unit-tests to the utility-function that did not exist before, as
the function was implicitly tested through clang-tidy checks. These
tests were not changed, as the API is still compatible.
Reviewers: aaron.ballman, hokein, alexfh, shuaiwang, lebedev.ri
Reviewed By: aaron.ballman
Subscribers: jdoerfert, mgorny, xazax.hun, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D54395
2020-01-04 03:36:49 +08:00
|
|
|
IncludeStyle);
|
2019-03-23 02:58:12 +08:00
|
|
|
PP->addPPCallbacks(Inserter->CreatePPCallbacks());
|
2016-07-02 04:12:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void UnnecessaryValueParamCheck::storeOptions(
|
|
|
|
ClangTidyOptions::OptionMap &Opts) {
|
|
|
|
Options.store(Opts, "IncludeStyle",
|
|
|
|
utils::IncludeSorter::toString(IncludeStyle));
|
2018-10-12 21:05:21 +08:00
|
|
|
Options.store(Opts, "AllowedTypes",
|
|
|
|
utils::options::serializeStringList(AllowedTypes));
|
2016-07-02 04:12:15 +08:00
|
|
|
}
|
|
|
|
|
2018-09-18 01:59:51 +08:00
|
|
|
void UnnecessaryValueParamCheck::onEndOfTranslationUnit() {
|
|
|
|
MutationAnalyzers.clear();
|
|
|
|
}
|
|
|
|
|
2016-07-02 04:12:15 +08:00
|
|
|
void UnnecessaryValueParamCheck::handleMoveFix(const ParmVarDecl &Var,
|
|
|
|
const DeclRefExpr &CopyArgument,
|
|
|
|
const ASTContext &Context) {
|
2018-08-10 06:42:26 +08:00
|
|
|
auto Diag = diag(CopyArgument.getBeginLoc(),
|
2016-07-02 04:12:15 +08:00
|
|
|
"parameter %0 is passed by value and only copied once; "
|
|
|
|
"consider moving it to avoid unnecessary copies")
|
|
|
|
<< &Var;
|
|
|
|
// Do not propose fixes in macros since we cannot place them correctly.
|
2018-08-10 06:42:26 +08:00
|
|
|
if (CopyArgument.getBeginLoc().isMacroID())
|
2016-07-02 04:12:15 +08:00
|
|
|
return;
|
|
|
|
const auto &SM = Context.getSourceManager();
|
2016-08-01 20:06:18 +08:00
|
|
|
auto EndLoc = Lexer::getLocForEndOfToken(CopyArgument.getLocation(), 0, SM,
|
|
|
|
Context.getLangOpts());
|
2018-08-10 06:42:26 +08:00
|
|
|
Diag << FixItHint::CreateInsertion(CopyArgument.getBeginLoc(), "std::move(")
|
2016-07-02 04:12:15 +08:00
|
|
|
<< FixItHint::CreateInsertion(EndLoc, ")");
|
|
|
|
if (auto IncludeFixit = Inserter->CreateIncludeInsertion(
|
2018-08-10 06:42:26 +08:00
|
|
|
SM.getFileID(CopyArgument.getBeginLoc()), "utility",
|
2016-07-02 04:12:15 +08:00
|
|
|
/*IsAngled=*/true))
|
|
|
|
Diag << *IncludeFixit;
|
|
|
|
}
|
|
|
|
|
2016-03-29 10:42:38 +08:00
|
|
|
} // namespace performance
|
|
|
|
} // namespace tidy
|
|
|
|
} // namespace clang
|