summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp
diff options
context:
space:
mode:
authorAlexander Kornienko <alexfh@google.com>2015-08-14 13:17:11 +0000
committerAlexander Kornienko <alexfh@google.com>2015-08-14 13:17:11 +0000
commitfc650864eff46017be759353dcd1d43e82fd0eb0 (patch)
treea580060a2076bbb18fb162dc30c44c11a657ff61 /clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp
parent62b81b875ab32940e9e5c0dd0a29b1a86b6bc0de (diff)
downloadbcm5719-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.cpp223
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
OpenPOWER on IntegriCloud