diff options
Diffstat (limited to 'clang-tools-extra/clang-tidy/modernize')
5 files changed, 334 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt new file mode 100644 index 00000000000..c53ccb3334a --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt @@ -0,0 +1,14 @@ +set(LLVM_LINK_COMPONENTS support) + +add_clang_library(clangTidyModernizeModule + ModernizeTidyModule.cpp + PassByValueCheck.cpp + + LINK_LIBS + clangAST + clangASTMatchers + clangBasic + clangLex + clangTidy + clangTidyReadabilityModule + ) diff --git a/clang-tools-extra/clang-tidy/modernize/Makefile b/clang-tools-extra/clang-tidy/modernize/Makefile new file mode 100644 index 00000000000..28cf9ef4538 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/Makefile @@ -0,0 +1,12 @@ +##===- clang-tidy/modernize/Makefile -----------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +CLANG_LEVEL := ../../../.. +LIBRARYNAME := clangTidyModernizeModule + +include $(CLANG_LEVEL)/Makefile diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp new file mode 100644 index 00000000000..5f25832e8a6 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -0,0 +1,46 @@ +//===--- ModernizeTidyModule.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 "../ClangTidy.h" +#include "../ClangTidyModule.h" +#include "../ClangTidyModuleRegistry.h" +#include "PassByValueCheck.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace modernize { + +class ModernizeModule : public ClangTidyModule { +public: + void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + CheckFactories.registerCheck<PassByValueCheck>("modernize-pass-by-value"); + } + + ClangTidyOptions getModuleOptions() override { + ClangTidyOptions Options; + auto &Opts = Options.CheckOptions; + Opts["modernize-pass-by-value.IncludeStyle"] = "llvm"; // Also: "google". + return Options; + } +}; + +// Register the ModernizeTidyModule using this statically initialized variable. +static ClangTidyModuleRegistry::Add<ModernizeModule> X("modernize-module", + "Add modernize checks."); + +} // namespace modernize + +// This anchor is used to force the linker to link in the generated object file +// and thus register the ModernizeModule. +volatile int ModernizeModuleAnchorSource = 0; + +} // namespace tidy +} // namespace clang 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 diff --git a/clang-tools-extra/clang-tidy/modernize/PassByValueCheck.h b/clang-tools-extra/clang-tidy/modernize/PassByValueCheck.h new file mode 100644 index 00000000000..df9b7cccbcd --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/PassByValueCheck.h @@ -0,0 +1,39 @@ +//===--- PassByValueCheck.h - clang-tidy-------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_PASS_BY_VALUE_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_PASS_BY_VALUE_H + +#include "../ClangTidy.h" +#include "../IncludeInserter.h" + +#include <memory> + +namespace clang { +namespace tidy { +namespace modernize { + +class PassByValueCheck : public ClangTidyCheck { +public: + PassByValueCheck(StringRef Name, ClangTidyContext *Context); + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void registerPPCallbacks(clang::CompilerInstance &Compiler) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + std::unique_ptr<IncludeInserter> Inserter; + const IncludeSorter::IncludeStyle IncludeStyle; +}; + +} // namespace modernize +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_PASS_BY_VALUE_H |

