forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			229 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			229 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			C++
		
	
	
	
//===--- PassByValueCheck.cpp - clang-tidy---------------------------------===//
 | 
						|
//
 | 
						|
// 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
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
#include "PassByValueCheck.h"
 | 
						|
#include "clang/AST/ASTContext.h"
 | 
						|
#include "clang/AST/RecursiveASTVisitor.h"
 | 
						|
#include "clang/ASTMatchers/ASTMatchFinder.h"
 | 
						|
#include "clang/ASTMatchers/ASTMatchers.h"
 | 
						|
#include "clang/Frontend/CompilerInstance.h"
 | 
						|
#include "clang/Lex/Lexer.h"
 | 
						|
#include "clang/Lex/Preprocessor.h"
 | 
						|
 | 
						|
using namespace clang::ast_matchers;
 | 
						|
using namespace llvm;
 | 
						|
 | 
						|
namespace clang {
 | 
						|
namespace tidy {
 | 
						|
namespace modernize {
 | 
						|
 | 
						|
namespace {
 | 
						|
/// Matches move-constructible classes.
 | 
						|
///
 | 
						|
/// Given
 | 
						|
/// \code
 | 
						|
///   // POD types are trivially move constructible.
 | 
						|
///   struct Foo { int a; };
 | 
						|
///
 | 
						|
///   struct Bar {
 | 
						|
///     Bar(Bar &&) = deleted;
 | 
						|
///     int a;
 | 
						|
///   };
 | 
						|
/// \endcode
 | 
						|
/// recordDecl(isMoveConstructible())
 | 
						|
///   matches "Foo".
 | 
						|
AST_MATCHER(CXXRecordDecl, isMoveConstructible) {
 | 
						|
  for (const CXXConstructorDecl *Ctor : Node.ctors()) {
 | 
						|
    if (Ctor->isMoveConstructor() && !Ctor->isDeleted())
 | 
						|
      return true;
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
} // namespace
 | 
						|
 | 
						|
static TypeMatcher notTemplateSpecConstRefType() {
 | 
						|
  return lValueReferenceType(
 | 
						|
      pointee(unless(templateSpecializationType()), isConstQualified()));
 | 
						|
}
 | 
						|
 | 
						|
static TypeMatcher nonConstValueType() {
 | 
						|
  return qualType(unless(anyOf(referenceType(), isConstQualified())));
 | 
						|
}
 | 
						|
 | 
						|
/// Whether or not \p ParamDecl is used exactly one time in \p Ctor.
 | 
						|
///
 | 
						|
/// Checks both in the init-list and the body of the constructor.
 | 
						|
static bool paramReferredExactlyOnce(const CXXConstructorDecl *Ctor,
 | 
						|
                                     const ParmVarDecl *ParamDecl) {
 | 
						|
  /// \c clang::RecursiveASTVisitor that checks that the given
 | 
						|
  /// \c ParmVarDecl is used exactly one time.
 | 
						|
  ///
 | 
						|
  /// \see ExactlyOneUsageVisitor::hasExactlyOneUsageIn()
 | 
						|
  class ExactlyOneUsageVisitor
 | 
						|
      : public RecursiveASTVisitor<ExactlyOneUsageVisitor> {
 | 
						|
    friend class RecursiveASTVisitor<ExactlyOneUsageVisitor>;
 | 
						|
 | 
						|
  public:
 | 
						|
    ExactlyOneUsageVisitor(const ParmVarDecl *ParamDecl)
 | 
						|
        : ParamDecl(ParamDecl) {}
 | 
						|
 | 
						|
    /// Whether or not the parameter variable is referred only once in
 | 
						|
    /// the
 | 
						|
    /// given constructor.
 | 
						|
    bool hasExactlyOneUsageIn(const CXXConstructorDecl *Ctor) {
 | 
						|
      Count = 0;
 | 
						|
      TraverseDecl(const_cast<CXXConstructorDecl *>(Ctor));
 | 
						|
      return Count == 1;
 | 
						|
    }
 | 
						|
 | 
						|
  private:
 | 
						|
    /// Counts the number of references to a variable.
 | 
						|
    ///
 | 
						|
    /// Stops the AST traversal if more than one usage is found.
 | 
						|
    bool VisitDeclRefExpr(DeclRefExpr *D) {
 | 
						|
      if (const ParmVarDecl *To = dyn_cast<ParmVarDecl>(D->getDecl())) {
 | 
						|
        if (To == ParamDecl) {
 | 
						|
          ++Count;
 | 
						|
          if (Count > 1) {
 | 
						|
            // No need to look further, used more than once.
 | 
						|
            return false;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
 | 
						|
    const ParmVarDecl *ParamDecl;
 | 
						|
    unsigned Count;
 | 
						|
  };
 | 
						|
 | 
						|
  return ExactlyOneUsageVisitor(ParamDecl).hasExactlyOneUsageIn(Ctor);
 | 
						|
}
 | 
						|
 | 
						|
/// Find all references to \p ParamDecl across all of the
 | 
						|
/// redeclarations of \p Ctor.
 | 
						|
static SmallVector<const ParmVarDecl *, 2>
 | 
						|
collectParamDecls(const CXXConstructorDecl *Ctor,
 | 
						|
                  const ParmVarDecl *ParamDecl) {
 | 
						|
  SmallVector<const ParmVarDecl *, 2> Results;
 | 
						|
  unsigned ParamIdx = ParamDecl->getFunctionScopeIndex();
 | 
						|
 | 
						|
  for (const FunctionDecl *Redecl : Ctor->redecls())
 | 
						|
    Results.push_back(Redecl->getParamDecl(ParamIdx));
 | 
						|
  return Results;
 | 
						|
}
 | 
						|
 | 
						|
PassByValueCheck::PassByValueCheck(StringRef Name, ClangTidyContext *Context)
 | 
						|
    : ClangTidyCheck(Name, Context),
 | 
						|
      IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
 | 
						|
          Options.getLocalOrGlobal("IncludeStyle", "llvm"))),
 | 
						|
      ValuesOnly(Options.get("ValuesOnly", 0) != 0) {}
 | 
						|
 | 
						|
void PassByValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
 | 
						|
  Options.store(Opts, "IncludeStyle",
 | 
						|
                utils::IncludeSorter::toString(IncludeStyle));
 | 
						|
  Options.store(Opts, "ValuesOnly", ValuesOnly);
 | 
						|
}
 | 
						|
 | 
						|
void PassByValueCheck::registerMatchers(MatchFinder *Finder) {
 | 
						|
  Finder->addMatcher(
 | 
						|
      cxxConstructorDecl(
 | 
						|
          forEachConstructorInitializer(
 | 
						|
              cxxCtorInitializer(
 | 
						|
                  unless(isBaseInitializer()),
 | 
						|
                  // Clang builds a CXXConstructExpr only when it knows which
 | 
						|
                  // constructor will be called. In dependent contexts a
 | 
						|
                  // ParenListExpr is generated instead of a CXXConstructExpr,
 | 
						|
                  // filtering out templates automatically for us.
 | 
						|
                  withInitializer(cxxConstructExpr(
 | 
						|
                      has(ignoringParenImpCasts(declRefExpr(
 | 
						|
                          to(parmVarDecl(
 | 
						|
                                 hasType(qualType(
 | 
						|
                                     // Match only const-ref or a non-const
 | 
						|
                                     // value parameters. Rvalues,
 | 
						|
                                     // TemplateSpecializationValues and
 | 
						|
                                     // const-values shouldn't be modified.
 | 
						|
                                     ValuesOnly
 | 
						|
                                         ? nonConstValueType()
 | 
						|
                                         : anyOf(notTemplateSpecConstRefType(),
 | 
						|
                                                 nonConstValueType()))))
 | 
						|
                                 .bind("Param"))))),
 | 
						|
                      hasDeclaration(cxxConstructorDecl(
 | 
						|
                          isCopyConstructor(), unless(isDeleted()),
 | 
						|
                          hasDeclContext(
 | 
						|
                              cxxRecordDecl(isMoveConstructible())))))))
 | 
						|
                  .bind("Initializer")))
 | 
						|
          .bind("Ctor"),
 | 
						|
      this);
 | 
						|
}
 | 
						|
 | 
						|
void PassByValueCheck::registerPPCallbacks(const SourceManager &SM,
 | 
						|
                                           Preprocessor *PP,
 | 
						|
                                           Preprocessor *ModuleExpanderPP) {
 | 
						|
    Inserter = std::make_unique<utils::IncludeInserter>(SM, getLangOpts(),
 | 
						|
                                                         IncludeStyle);
 | 
						|
    PP->addPPCallbacks(Inserter->CreatePPCallbacks());
 | 
						|
}
 | 
						|
 | 
						|
void PassByValueCheck::check(const MatchFinder::MatchResult &Result) {
 | 
						|
  const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("Ctor");
 | 
						|
  const auto *ParamDecl = Result.Nodes.getNodeAs<ParmVarDecl>("Param");
 | 
						|
  const auto *Initializer =
 | 
						|
      Result.Nodes.getNodeAs<CXXCtorInitializer>("Initializer");
 | 
						|
  SourceManager &SM = *Result.SourceManager;
 | 
						|
 | 
						|
  // If the parameter is used or anything other than the copy, do not apply
 | 
						|
  // the changes.
 | 
						|
  if (!paramReferredExactlyOnce(Ctor, ParamDecl))
 | 
						|
    return;
 | 
						|
 | 
						|
  // If the parameter is trivial to copy, don't move it. Moving a trivivally
 | 
						|
  // copyable type will cause a problem with performance-move-const-arg
 | 
						|
  if (ParamDecl->getType().getNonReferenceType().isTriviallyCopyableType(
 | 
						|
          *Result.Context))
 | 
						|
    return;
 | 
						|
 | 
						|
  auto Diag = diag(ParamDecl->getBeginLoc(), "pass by value and use std::move");
 | 
						|
 | 
						|
  // Iterate over all declarations of the constructor.
 | 
						|
  for (const ParmVarDecl *ParmDecl : collectParamDecls(Ctor, ParamDecl)) {
 | 
						|
    auto ParamTL = ParmDecl->getTypeSourceInfo()->getTypeLoc();
 | 
						|
    auto RefTL = ParamTL.getAs<ReferenceTypeLoc>();
 | 
						|
 | 
						|
    // Do not replace if it is already a value, skip.
 | 
						|
    if (RefTL.isNull())
 | 
						|
      continue;
 | 
						|
 | 
						|
    TypeLoc ValueTL = RefTL.getPointeeLoc();
 | 
						|
    auto TypeRange = CharSourceRange::getTokenRange(ParmDecl->getBeginLoc(),
 | 
						|
                                                    ParamTL.getEndLoc());
 | 
						|
    std::string ValueStr = Lexer::getSourceText(CharSourceRange::getTokenRange(
 | 
						|
                                                    ValueTL.getSourceRange()),
 | 
						|
                                                SM, getLangOpts())
 | 
						|
                               .str();
 | 
						|
    ValueStr += ' ';
 | 
						|
    Diag << FixItHint::CreateReplacement(TypeRange, ValueStr);
 | 
						|
  }
 | 
						|
 | 
						|
  // Use std::move in the initialization list.
 | 
						|
  Diag << FixItHint::CreateInsertion(Initializer->getRParenLoc(), ")")
 | 
						|
       << FixItHint::CreateInsertion(
 | 
						|
              Initializer->getLParenLoc().getLocWithOffset(1), "std::move(");
 | 
						|
 | 
						|
  if (auto IncludeFixit = Inserter->CreateIncludeInsertion(
 | 
						|
          Result.SourceManager->getFileID(Initializer->getSourceLocation()),
 | 
						|
          "utility",
 | 
						|
          /*IsAngled=*/true)) {
 | 
						|
    Diag << *IncludeFixit;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
} // namespace modernize
 | 
						|
} // namespace tidy
 | 
						|
} // namespace clang
 |