brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.1 KiB · d626297 Raw
192 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "UnnecessaryValueParamCheck.h"10#include "../utils/DeclRefExprUtils.h"11#include "../utils/FixItHintUtils.h"12#include "../utils/Matchers.h"13#include "../utils/OptionsUtils.h"14#include "../utils/TypeTraits.h"15#include "clang/Frontend/CompilerInstance.h"16#include "clang/Lex/Lexer.h"17#include "clang/Lex/Preprocessor.h"18#include <optional>19 20using namespace clang::ast_matchers;21 22namespace clang::tidy::performance {23 24static std::string paramNameOrIndex(StringRef Name, size_t Index) {25  return (Name.empty() ? llvm::Twine('#') + llvm::Twine(Index + 1)26                       : llvm::Twine('\'') + Name + llvm::Twine('\''))27      .str();28}29 30static bool hasLoopStmtAncestor(const DeclRefExpr &DeclRef, const Decl &Decl,31                                ASTContext &Context) {32  auto Matches = match(33      traverse(TK_AsIs,34               decl(forEachDescendant(declRefExpr(35                   equalsNode(&DeclRef),36                   unless(hasAncestor(stmt(anyOf(forStmt(), cxxForRangeStmt(),37                                                 whileStmt(), doStmt())))))))),38      Decl, Context);39  return Matches.empty();40}41 42UnnecessaryValueParamCheck::UnnecessaryValueParamCheck(43    StringRef Name, ClangTidyContext *Context)44    : ClangTidyCheck(Name, Context),45      Inserter(Options.getLocalOrGlobal("IncludeStyle",46                                        utils::IncludeSorter::IS_LLVM),47               areDiagsSelfContained()),48      AllowedTypes(49          utils::options::parseStringList(Options.get("AllowedTypes", ""))),50      IgnoreCoroutines(Options.get("IgnoreCoroutines", true)) {}51 52void UnnecessaryValueParamCheck::registerMatchers(MatchFinder *Finder) {53  const auto ExpensiveValueParamDecl = parmVarDecl(54      hasType(qualType(55          hasCanonicalType(matchers::isExpensiveToCopy()),56          unless(anyOf(hasCanonicalType(referenceType()),57                       hasDeclaration(namedDecl(58                           matchers::matchesAnyListedName(AllowedTypes))))))),59      decl().bind("param"));60  Finder->addMatcher(61      traverse(TK_AsIs,62               functionDecl(63                   hasBody(IgnoreCoroutines ? stmt(unless(coroutineBodyStmt()))64                                            : stmt()),65                   isDefinition(), unless(isImplicit()),66                   unless(cxxMethodDecl(anyOf(isOverride(), isFinal()))),67                   has(typeLoc(forEach(ExpensiveValueParamDecl))),68                   decl().bind("functionDecl"))),69      this);70}71 72void UnnecessaryValueParamCheck::check(const MatchFinder::MatchResult &Result) {73  const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param");74  const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("functionDecl");75 76  const TraversalKindScope RAII(*Result.Context, TK_AsIs);77 78  FunctionParmMutationAnalyzer *Analyzer =79      FunctionParmMutationAnalyzer::getFunctionParmMutationAnalyzer(80          *Function, *Result.Context, MutationAnalyzerCache);81  if (Analyzer->isMutated(Param))82    return;83 84  const bool IsConstQualified =85      Param->getType().getCanonicalType().isConstQualified();86 87  // If the parameter is non-const, check if it has a move constructor and is88  // only referenced once to copy-construct another object or whether it has a89  // move assignment operator and is only referenced once when copy-assigned.90  // In this case wrap DeclRefExpr with std::move() to avoid the unnecessary91  // copy.92  if (!IsConstQualified) {93    auto AllDeclRefExprs = utils::decl_ref_expr::allDeclRefExprs(94        *Param, *Function, *Result.Context);95    if (AllDeclRefExprs.size() == 1) {96      auto CanonicalType = Param->getType().getCanonicalType();97      const auto &DeclRefExpr = **AllDeclRefExprs.begin();98 99      if (!hasLoopStmtAncestor(DeclRefExpr, *Function, *Result.Context) &&100          ((utils::type_traits::hasNonTrivialMoveConstructor(CanonicalType) &&101            utils::decl_ref_expr::isCopyConstructorArgument(102                DeclRefExpr, *Function, *Result.Context)) ||103           (utils::type_traits::hasNonTrivialMoveAssignment(CanonicalType) &&104            utils::decl_ref_expr::isCopyAssignmentArgument(105                DeclRefExpr, *Function, *Result.Context)))) {106        handleMoveFix(*Param, DeclRefExpr, *Result.Context);107        return;108      }109    }110  }111 112  handleConstRefFix(*Function, *Param, *Result.Context);113}114 115void UnnecessaryValueParamCheck::registerPPCallbacks(116    const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {117  Inserter.registerPreprocessor(PP);118}119 120void UnnecessaryValueParamCheck::storeOptions(121    ClangTidyOptions::OptionMap &Opts) {122  Options.store(Opts, "IncludeStyle", Inserter.getStyle());123  Options.store(Opts, "AllowedTypes",124                utils::options::serializeStringList(AllowedTypes));125  Options.store(Opts, "IgnoreCoroutines", IgnoreCoroutines);126}127 128void UnnecessaryValueParamCheck::onEndOfTranslationUnit() {129  MutationAnalyzerCache.clear();130}131 132void UnnecessaryValueParamCheck::handleConstRefFix(const FunctionDecl &Function,133                                                   const ParmVarDecl &Param,134                                                   ASTContext &Context) {135  const size_t Index =136      llvm::find(Function.parameters(), &Param) - Function.parameters().begin();137  const bool IsConstQualified =138      Param.getType().getCanonicalType().isConstQualified();139 140  auto Diag =141      diag(Param.getLocation(),142           "the %select{|const qualified }0parameter %1 of type %2 is copied "143           "for each "144           "invocation%select{ but only used as a const reference|}0; consider "145           "making it a %select{const |}0reference")146      << IsConstQualified << paramNameOrIndex(Param.getName(), Index)147      << Param.getType();148  // Do not propose fixes when:149  // 1. the ParmVarDecl is in a macro, since we cannot place them correctly150  // 2. the function is virtual as it might break overrides151  // 3. the function is an explicit template/ specialization.152  const auto *Method = llvm::dyn_cast<CXXMethodDecl>(&Function);153  if (Param.getBeginLoc().isMacroID() || (Method && Method->isVirtual()) ||154      Function.getTemplateSpecializationKind() == TSK_ExplicitSpecialization)155    return;156  for (const auto *FunctionDecl = &Function; FunctionDecl != nullptr;157       FunctionDecl = FunctionDecl->getPreviousDecl()) {158    const auto &CurrentParam = *FunctionDecl->getParamDecl(Index);159    Diag << utils::fixit::changeVarDeclToReference(CurrentParam, Context);160    // The parameter of each declaration needs to be checked individually as to161    // whether it is const or not as constness can differ between definition and162    // declaration.163    if (!CurrentParam.getType().getCanonicalType().isConstQualified()) {164      if (std::optional<FixItHint> Fix = utils::fixit::addQualifierToVarDecl(165              CurrentParam, Context, Qualifiers::Const))166        Diag << *Fix;167    }168  }169}170 171void UnnecessaryValueParamCheck::handleMoveFix(const ParmVarDecl &Param,172                                               const DeclRefExpr &CopyArgument,173                                               ASTContext &Context) {174  auto Diag =175      diag(CopyArgument.getBeginLoc(),176           "parameter %0 of type %1 is passed by value and only copied once; "177           "consider moving it to avoid unnecessary copies")178      << &Param << Param.getType();179  // Do not propose fixes in macros since we cannot place them correctly.180  if (CopyArgument.getBeginLoc().isMacroID())181    return;182  const auto &SM = Context.getSourceManager();183  auto EndLoc = Lexer::getLocForEndOfToken(CopyArgument.getLocation(), 0, SM,184                                           Context.getLangOpts());185  Diag << FixItHint::CreateInsertion(CopyArgument.getBeginLoc(), "std::move(")186       << FixItHint::CreateInsertion(EndLoc, ")")187       << Inserter.createIncludeInsertion(188              SM.getFileID(CopyArgument.getBeginLoc()), "<utility>");189}190 191} // namespace clang::tidy::performance192