diff options
author | Alexander Kornienko <alexfh@google.com> | 2015-08-14 13:17:11 +0000 |
---|---|---|
committer | Alexander Kornienko <alexfh@google.com> | 2015-08-14 13:17:11 +0000 |
commit | fc650864eff46017be759353dcd1d43e82fd0eb0 (patch) | |
tree | a580060a2076bbb18fb162dc30c44c11a657ff61 /clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp | |
parent | 62b81b875ab32940e9e5c0dd0a29b1a86b6bc0de (diff) | |
download | bcm5719-llvm-fc650864eff46017be759353dcd1d43e82fd0eb0.tar.gz bcm5719-llvm-fc650864eff46017be759353dcd1d43e82fd0eb0.zip |
[clang-tidy] Create clang-tidy module modernize. Add pass-by-value check.
This is the first step for migrating cppmodernize to clang-tidy.
http://reviews.llvm.org/D11946
Patch by Angel Garcia!
llvm-svn: 245045
Diffstat (limited to 'clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp')
-rw-r--r-- | clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp b/clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp new file mode 100644 index 00000000000..d6d750b07d3 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp @@ -0,0 +1,223 @@ +//===--- PassByValueCheck.cpp - clang-tidy---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#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 { + +/// \brief 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; +} + +/// \brief Matches non-deleted copy constructors. +/// +/// Given +/// \code +/// struct Foo { Foo(const Foo &) = default; }; +/// struct Bar { Bar(const Bar &) = deleted; }; +/// \endcode +/// constructorDecl(isNonDeletedCopyConstructor()) +/// matches "Foo(const Foo &)". +AST_MATCHER(CXXConstructorDecl, isNonDeletedCopyConstructor) { + return Node.isCopyConstructor() && !Node.isDeleted(); +} + +static TypeMatcher constRefType() { + return lValueReferenceType(pointee(isConstQualified())); +} + +static TypeMatcher nonConstValueType() { + return qualType(unless(anyOf(referenceType(), isConstQualified()))); +} + +/// \brief 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) { + /// \brief \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) {} + + /// \brief 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: + /// \brief 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); +} + +/// \brief 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(Options.get("IncludeStyle", "llvm") == "llvm" ? + IncludeSorter::IS_LLVM : IncludeSorter::IS_Google) {} + +void PassByValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "IncludeStyle", + IncludeStyle == IncludeSorter::IS_LLVM ? "llvm" : "google"); +} + +void PassByValueCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + constructorDecl( + forEachConstructorInitializer( + ctorInitializer( + // Clang builds a CXXConstructExpr only whin 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(constructExpr( + has(declRefExpr(to( + parmVarDecl( + hasType(qualType( + // Match only const-ref or a non-const value + // parameters. Rvalues and const-values + // shouldn't be modified. + anyOf(constRefType(), nonConstValueType())))) + .bind("Param")))), + hasDeclaration(constructorDecl( + isNonDeletedCopyConstructor(), + hasDeclContext(recordDecl(isMoveConstructible()))))))) + .bind("Initializer"))) + .bind("Ctor"), + this); +} + +void PassByValueCheck::registerPPCallbacks(CompilerInstance &Compiler) { + Inserter.reset(new IncludeInserter(Compiler.getSourceManager(), + Compiler.getLangOpts(), IncludeStyle)); + Compiler.getPreprocessor().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; + + auto Diag = diag(ParamDecl->getLocStart(), "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->getLocStart(), + ParamTL.getLocEnd()); + std::string ValueStr = + Lexer::getSourceText( + CharSourceRange::getTokenRange(ValueTL.getSourceRange()), SM, + Result.Context->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("); + + auto Insertion = + Inserter->CreateIncludeInsertion(SM.getMainFileID(), "utility", + /*IsAngled=*/true); + if (Insertion.hasValue()) + Diag << Insertion.getValue(); +} + +} // namespace modernize +} // namespace tidy +} // namespace clang |