Imported Upstream version 6.10.0.49

Former-commit-id: 1d6753294b2993e1fbf92de9366bb9544db4189b
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2020-01-16 16:38:04 +00:00
parent d94e79959b
commit 468663ddbb
48518 changed files with 2789335 additions and 61176 deletions

View File

@@ -0,0 +1,25 @@
set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangTidyPerformanceModule
FasterStringFindCheck.cpp
ForRangeCopyCheck.cpp
ImplicitConversionInLoopCheck.cpp
InefficientAlgorithmCheck.cpp
InefficientStringConcatenationCheck.cpp
InefficientVectorOperationCheck.cpp
MoveConstArgCheck.cpp
MoveConstructorInitCheck.cpp
NoexceptMoveConstructorCheck.cpp
PerformanceTidyModule.cpp
TypePromotionInMathFnCheck.cpp
UnnecessaryCopyInitialization.cpp
UnnecessaryValueParamCheck.cpp
LINK_LIBS
clangAST
clangASTMatchers
clangBasic
clangLex
clangTidy
clangTidyUtils
)

View File

@@ -0,0 +1,104 @@
//===--- FasterStringFindCheck.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 "FasterStringFindCheck.h"
#include "../utils/OptionsUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "llvm/ADT/Optional.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace performance {
namespace {
llvm::Optional<std::string> MakeCharacterLiteral(const StringLiteral *Literal) {
std::string Result;
{
llvm::raw_string_ostream OS(Result);
Literal->outputString(OS);
}
// Now replace the " with '.
auto pos = Result.find_first_of('"');
if (pos == Result.npos)
return llvm::None;
Result[pos] = '\'';
pos = Result.find_last_of('"');
if (pos == Result.npos)
return llvm::None;
Result[pos] = '\'';
return Result;
}
AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher<Expr>,
hasSubstitutedType) {
return hasType(qualType(anyOf(substTemplateTypeParmType(),
hasDescendant(substTemplateTypeParmType()))));
}
} // namespace
FasterStringFindCheck::FasterStringFindCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
StringLikeClasses(utils::options::parseStringList(
Options.get("StringLikeClasses", "std::basic_string"))) {}
void FasterStringFindCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "StringLikeClasses",
utils::options::serializeStringList(StringLikeClasses));
}
void FasterStringFindCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)
return;
const auto SingleChar =
expr(ignoringParenCasts(stringLiteral(hasSize(1)).bind("literal")));
const auto StringFindFunctions =
hasAnyName("find", "rfind", "find_first_of", "find_first_not_of",
"find_last_of", "find_last_not_of");
Finder->addMatcher(
cxxMemberCallExpr(
callee(functionDecl(StringFindFunctions).bind("func")),
anyOf(argumentCountIs(1), argumentCountIs(2)),
hasArgument(0, SingleChar),
on(expr(
hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
recordDecl(hasAnyName(SmallVector<StringRef, 4>(
StringLikeClasses.begin(), StringLikeClasses.end()))))))),
unless(hasSubstitutedType())))),
this);
}
void FasterStringFindCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>("literal");
const auto *FindFunc = Result.Nodes.getNodeAs<FunctionDecl>("func");
auto Replacement = MakeCharacterLiteral(Literal);
if (!Replacement)
return;
diag(Literal->getLocStart(), "%0 called with a string literal consisting of "
"a single character; consider using the more "
"effective overload accepting a character")
<< FindFunc << FixItHint::CreateReplacement(
CharSourceRange::getTokenRange(Literal->getLocStart(),
Literal->getLocEnd()),
*Replacement);
}
} // namespace performance
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,43 @@
//===--- FasterStringFindCheck.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_PERFORMANCE_FASTER_STRING_FIND_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_FASTER_STRING_FIND_H
#include "../ClangTidy.h"
#include <string>
#include <vector>
namespace clang {
namespace tidy {
namespace performance {
/// Optimize calls to std::string::find() and friends when the needle passed is
/// a single character string literal.
/// The character literal overload is more efficient.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/performance-faster-string-find.html
class FasterStringFindCheck : public ClangTidyCheck {
public:
FasterStringFindCheck(StringRef Name, ClangTidyContext *Context);
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
private:
const std::vector<std::string> StringLikeClasses;
};
} // namespace performance
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_FASTER_STRING_FIND_H

View File

@@ -0,0 +1,95 @@
//===--- ForRangeCopyCheck.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 "ForRangeCopyCheck.h"
#include "../utils/DeclRefExprUtils.h"
#include "../utils/FixItHintUtils.h"
#include "../utils/TypeTraits.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace performance {
ForRangeCopyCheck::ForRangeCopyCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
WarnOnAllAutoCopies(Options.get("WarnOnAllAutoCopies", 0)) {}
void ForRangeCopyCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "WarnOnAllAutoCopies", WarnOnAllAutoCopies);
}
void ForRangeCopyCheck::registerMatchers(MatchFinder *Finder) {
// Match loop variables that are not references or pointers or are already
// initialized through MaterializeTemporaryExpr which indicates a type
// conversion.
auto LoopVar = varDecl(
hasType(hasCanonicalType(unless(anyOf(referenceType(), pointerType())))),
unless(hasInitializer(expr(hasDescendant(materializeTemporaryExpr())))));
Finder->addMatcher(cxxForRangeStmt(hasLoopVariable(LoopVar.bind("loopVar")))
.bind("forRange"),
this);
}
void ForRangeCopyCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Var = Result.Nodes.getNodeAs<VarDecl>("loopVar");
// Ignore code in macros since we can't place the fixes correctly.
if (Var->getLocStart().isMacroID())
return;
if (handleConstValueCopy(*Var, *Result.Context))
return;
const auto *ForRange = Result.Nodes.getNodeAs<CXXForRangeStmt>("forRange");
handleCopyIsOnlyConstReferenced(*Var, *ForRange, *Result.Context);
}
bool ForRangeCopyCheck::handleConstValueCopy(const VarDecl &LoopVar,
ASTContext &Context) {
if (WarnOnAllAutoCopies) {
// For aggressive check just test that loop variable has auto type.
if (!isa<AutoType>(LoopVar.getType()))
return false;
} else if (!LoopVar.getType().isConstQualified()) {
return false;
}
llvm::Optional<bool> Expensive =
utils::type_traits::isExpensiveToCopy(LoopVar.getType(), Context);
if (!Expensive || !*Expensive)
return false;
auto Diagnostic =
diag(LoopVar.getLocation(),
"the loop variable's type is not a reference type; this creates a "
"copy in each iteration; consider making this a reference")
<< utils::fixit::changeVarDeclToReference(LoopVar, Context);
if (!LoopVar.getType().isConstQualified())
Diagnostic << utils::fixit::changeVarDeclToConst(LoopVar);
return true;
}
bool ForRangeCopyCheck::handleCopyIsOnlyConstReferenced(
const VarDecl &LoopVar, const CXXForRangeStmt &ForRange,
ASTContext &Context) {
llvm::Optional<bool> Expensive =
utils::type_traits::isExpensiveToCopy(LoopVar.getType(), Context);
if (LoopVar.getType().isConstQualified() || !Expensive || !*Expensive)
return false;
if (!utils::decl_ref_expr::isOnlyUsedAsConst(LoopVar, *ForRange.getBody(),
Context))
return false;
diag(LoopVar.getLocation(),
"loop variable is copied but only used as const reference; consider "
"making it a const reference")
<< utils::fixit::changeVarDeclToConst(LoopVar)
<< utils::fixit::changeVarDeclToReference(LoopVar, Context);
return true;
}
} // namespace performance
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,49 @@
//===--- ForRangeCopyCheck.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_PERFORMANCE_FORRANGECOPYCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_FORRANGECOPYCHECK_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace performance {
/// A check that detects copied loop variables and suggests using const
/// references.
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/performance-for-range-copy.html
class ForRangeCopyCheck : public ClangTidyCheck {
public:
ForRangeCopyCheck(StringRef Name, ClangTidyContext *Context);
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
// Checks if the loop variable is a const value and expensive to copy. If so
// suggests it be converted to a const reference.
bool handleConstValueCopy(const VarDecl &LoopVar, ASTContext &Context);
// Checks if the loop variable is a non-const value and whether only
// const methods are invoked on it or whether it is only used as a const
// reference argument. If so it suggests it be made a const reference.
bool handleCopyIsOnlyConstReferenced(const VarDecl &LoopVar,
const CXXForRangeStmt &ForRange,
ASTContext &Context);
const bool WarnOnAllAutoCopies;
};
} // namespace performance
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_FORRANGECOPYCHECK_H

View File

@@ -0,0 +1,98 @@
//===--- ImplicitConversionInLoopCheck.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 "ImplicitConversionInLoopCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Lex/Lexer.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace performance {
// Checks if the stmt is a ImplicitCastExpr with a CastKind that is not a NoOp.
// The subtelty is that in some cases (user defined conversions), we can
// get to ImplicitCastExpr inside each other, with the outer one a NoOp. In this
// case we skip the first cast expr.
static bool IsNonTrivialImplicitCast(const Stmt *ST) {
if (const auto *ICE = dyn_cast<ImplicitCastExpr>(ST)) {
return (ICE->getCastKind() != CK_NoOp) ||
IsNonTrivialImplicitCast(ICE->getSubExpr());
}
return false;
}
void ImplicitConversionInLoopCheck::registerMatchers(MatchFinder *Finder) {
// We look for const ref loop variables that (optionally inside an
// ExprWithCleanup) materialize a temporary, and contain a implicit
// conversion. The check on the implicit conversion is done in check() because
// we can't access implicit conversion subnode via matchers: has() skips casts
// and materialize! We also bind on the call to operator* to get the proper
// type in the diagnostic message.
//
// Note that when the implicit conversion is done through a user defined
// conversion operator, the node is a CXXMemberCallExpr, not a
// CXXOperatorCallExpr, so it should not get caught by the
// cxxOperatorCallExpr() matcher.
Finder->addMatcher(
cxxForRangeStmt(hasLoopVariable(
varDecl(hasType(qualType(references(qualType(isConstQualified())))),
hasInitializer(expr(hasDescendant(cxxOperatorCallExpr().bind(
"operator-call")))
.bind("init")))
.bind("faulty-var"))),
this);
}
void ImplicitConversionInLoopCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *VD = Result.Nodes.getNodeAs<VarDecl>("faulty-var");
const auto *Init = Result.Nodes.getNodeAs<Expr>("init");
const auto *OperatorCall =
Result.Nodes.getNodeAs<CXXOperatorCallExpr>("operator-call");
if (const auto *Cleanup = dyn_cast<ExprWithCleanups>(Init))
Init = Cleanup->getSubExpr();
const auto *Materialized = dyn_cast<MaterializeTemporaryExpr>(Init);
if (!Materialized)
return;
// We ignore NoOp casts. Those are generated if the * operator on the
// iterator returns a value instead of a reference, and the loop variable
// is a reference. This situation is fine (it probably produces the same
// code at the end).
if (IsNonTrivialImplicitCast(Materialized->getTemporary()))
ReportAndFix(Result.Context, VD, OperatorCall);
}
void ImplicitConversionInLoopCheck::ReportAndFix(
const ASTContext *Context, const VarDecl *VD,
const CXXOperatorCallExpr *OperatorCall) {
// We only match on const ref, so we should print a const ref version of the
// type.
QualType ConstType = OperatorCall->getType().withConst();
QualType ConstRefType = Context->getLValueReferenceType(ConstType);
const char Message[] =
"the type of the loop variable %0 is different from the one returned "
"by the iterator and generates an implicit conversion; you can either "
"change the type to the matching one (%1 but 'const auto&' is always a "
"valid option) or remove the reference to make it explicit that you are "
"creating a new value";
diag(VD->getLocStart(), Message) << VD << ConstRefType;
}
} // namespace performance
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,38 @@
//===--- ImplicitConversionInLoopCheck.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_PERFORMANCE_IMPLICIT_CONVERSION_IN_LOOP_CHECK_H_
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_IMPLICIT_CONVERSION_IN_LOOP_CHECK_H_
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace performance {
// Checks that in a for range loop, if the provided type is a reference, then
// the underlying type is the one returned by the iterator (i.e. that there
// isn't any implicit conversion).
class ImplicitConversionInLoopCheck : public ClangTidyCheck {
public:
ImplicitConversionInLoopCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
void ReportAndFix(const ASTContext *Context, const VarDecl *VD,
const CXXOperatorCallExpr *OperatorCall);
};
} // namespace performance
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_IMPLICIT_CONVERSION_IN_LOOP_CHECK_H_

View File

@@ -0,0 +1,163 @@
//===--- InefficientAlgorithmCheck.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 "InefficientAlgorithmCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace performance {
static bool areTypesCompatible(QualType Left, QualType Right) {
if (const auto *LeftRefType = Left->getAs<ReferenceType>())
Left = LeftRefType->getPointeeType();
if (const auto *RightRefType = Right->getAs<ReferenceType>())
Right = RightRefType->getPointeeType();
return Left->getCanonicalTypeUnqualified() ==
Right->getCanonicalTypeUnqualified();
}
void InefficientAlgorithmCheck::registerMatchers(MatchFinder *Finder) {
// Only register the matchers for C++; the functionality currently does not
// provide any benefit to other languages, despite being benign.
if (!getLangOpts().CPlusPlus)
return;
const auto Algorithms =
hasAnyName("::std::find", "::std::count", "::std::equal_range",
"::std::lower_bound", "::std::upper_bound");
const auto ContainerMatcher = classTemplateSpecializationDecl(hasAnyName(
"::std::set", "::std::map", "::std::multiset", "::std::multimap",
"::std::unordered_set", "::std::unordered_map",
"::std::unordered_multiset", "::std::unordered_multimap"));
const auto Matcher =
callExpr(
callee(functionDecl(Algorithms)),
hasArgument(
0, cxxConstructExpr(has(ignoringParenImpCasts(cxxMemberCallExpr(
callee(cxxMethodDecl(hasName("begin"))),
on(declRefExpr(
hasDeclaration(decl().bind("IneffContObj")),
anyOf(hasType(ContainerMatcher.bind("IneffCont")),
hasType(pointsTo(
ContainerMatcher.bind("IneffContPtr")))))
.bind("IneffContExpr"))))))),
hasArgument(
1, cxxConstructExpr(has(ignoringParenImpCasts(cxxMemberCallExpr(
callee(cxxMethodDecl(hasName("end"))),
on(declRefExpr(
hasDeclaration(equalsBoundNode("IneffContObj"))))))))),
hasArgument(2, expr().bind("AlgParam")),
unless(isInTemplateInstantiation()))
.bind("IneffAlg");
Finder->addMatcher(Matcher, this);
}
void InefficientAlgorithmCheck::check(const MatchFinder::MatchResult &Result) {
const auto *AlgCall = Result.Nodes.getNodeAs<CallExpr>("IneffAlg");
const auto *IneffCont =
Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("IneffCont");
bool PtrToContainer = false;
if (!IneffCont) {
IneffCont =
Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("IneffContPtr");
PtrToContainer = true;
}
const llvm::StringRef IneffContName = IneffCont->getName();
const bool Unordered =
IneffContName.find("unordered") != llvm::StringRef::npos;
const bool Maplike = IneffContName.find("map") != llvm::StringRef::npos;
// Store if the key type of the container is compatible with the value
// that is searched for.
QualType ValueType = AlgCall->getArg(2)->getType();
QualType KeyType =
IneffCont->getTemplateArgs()[0].getAsType().getCanonicalType();
const bool CompatibleTypes = areTypesCompatible(KeyType, ValueType);
// Check if the comparison type for the algorithm and the container matches.
if (AlgCall->getNumArgs() == 4 && !Unordered) {
const Expr *Arg = AlgCall->getArg(3);
const QualType AlgCmp =
Arg->getType().getUnqualifiedType().getCanonicalType();
const unsigned CmpPosition =
(IneffContName.find("map") == llvm::StringRef::npos) ? 1 : 2;
const QualType ContainerCmp = IneffCont->getTemplateArgs()[CmpPosition]
.getAsType()
.getUnqualifiedType()
.getCanonicalType();
if (AlgCmp != ContainerCmp) {
diag(Arg->getLocStart(),
"different comparers used in the algorithm and the container");
return;
}
}
const auto *AlgDecl = AlgCall->getDirectCallee();
if (!AlgDecl)
return;
if (Unordered && AlgDecl->getName().find("bound") != llvm::StringRef::npos)
return;
const auto *AlgParam = Result.Nodes.getNodeAs<Expr>("AlgParam");
const auto *IneffContExpr = Result.Nodes.getNodeAs<Expr>("IneffContExpr");
FixItHint Hint;
SourceManager &SM = *Result.SourceManager;
LangOptions LangOpts = getLangOpts();
CharSourceRange CallRange =
CharSourceRange::getTokenRange(AlgCall->getSourceRange());
// FIXME: Create a common utility to extract a file range that the given token
// sequence is exactly spelled at (without macro argument expansions etc.).
// We can't use Lexer::makeFileCharRange here, because for
//
// #define F(x) x
// x(a b c);
//
// it will return "x(a b c)", when given the range "a"-"c". It makes sense for
// removals, but not for replacements.
//
// This code is over-simplified, but works for many real cases.
if (SM.isMacroArgExpansion(CallRange.getBegin()) &&
SM.isMacroArgExpansion(CallRange.getEnd())) {
CallRange.setBegin(SM.getSpellingLoc(CallRange.getBegin()));
CallRange.setEnd(SM.getSpellingLoc(CallRange.getEnd()));
}
if (!CallRange.getBegin().isMacroID() && !Maplike && CompatibleTypes) {
StringRef ContainerText = Lexer::getSourceText(
CharSourceRange::getTokenRange(IneffContExpr->getSourceRange()), SM,
LangOpts);
StringRef ParamText = Lexer::getSourceText(
CharSourceRange::getTokenRange(AlgParam->getSourceRange()), SM,
LangOpts);
std::string ReplacementText =
(llvm::Twine(ContainerText) + (PtrToContainer ? "->" : ".") +
AlgDecl->getName() + "(" + ParamText + ")")
.str();
Hint = FixItHint::CreateReplacement(CallRange, ReplacementText);
}
diag(AlgCall->getLocStart(),
"this STL algorithm call should be replaced with a container method")
<< Hint;
}
} // namespace performance
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,36 @@
//===--- InefficientAlgorithmCheck.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_PERFORMANCE_INEFFICIENTALGORITHMCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_INEFFICIENTALGORITHMCHECK_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace performance {
/// Warns on inefficient use of STL algorithms on associative containers.
///
/// Associative containers implements some of the algorithms as methods which
/// should be preferred to the algorithms in the algorithm header. The methods
/// can take advanatage of the order of the elements.
class InefficientAlgorithmCheck : public ClangTidyCheck {
public:
InefficientAlgorithmCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace performance
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_INEFFICIENTALGORITHMCHECK_H

View File

@@ -0,0 +1,88 @@
//===--- InefficientStringConcatenationCheck.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 "InefficientStringConcatenationCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace performance {
void InefficientStringConcatenationCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "StrictMode", StrictMode);
}
InefficientStringConcatenationCheck::InefficientStringConcatenationCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
StrictMode(Options.getLocalOrGlobal("StrictMode", 0)) {}
void InefficientStringConcatenationCheck::registerMatchers(
MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)
return;
const auto BasicStringType =
hasType(qualType(hasUnqualifiedDesugaredType(recordType(
hasDeclaration(cxxRecordDecl(hasName("::std::basic_string")))))));
const auto BasicStringPlusOperator = cxxOperatorCallExpr(
hasOverloadedOperatorName("+"),
hasAnyArgument(ignoringImpCasts(declRefExpr(BasicStringType))));
const auto PlusOperator =
cxxOperatorCallExpr(
hasOverloadedOperatorName("+"),
hasAnyArgument(ignoringImpCasts(declRefExpr(BasicStringType))),
hasDescendant(BasicStringPlusOperator))
.bind("plusOperator");
const auto AssignOperator = cxxOperatorCallExpr(
hasOverloadedOperatorName("="),
hasArgument(0, declRefExpr(BasicStringType,
hasDeclaration(decl().bind("lhsStrT")))
.bind("lhsStr")),
hasArgument(1, stmt(hasDescendant(declRefExpr(
hasDeclaration(decl(equalsBoundNode("lhsStrT"))))))),
hasDescendant(BasicStringPlusOperator));
if (StrictMode) {
Finder->addMatcher(cxxOperatorCallExpr(anyOf(AssignOperator, PlusOperator)),
this);
} else {
Finder->addMatcher(
cxxOperatorCallExpr(anyOf(AssignOperator, PlusOperator),
hasAncestor(stmt(anyOf(cxxForRangeStmt(),
whileStmt(), forStmt())))),
this);
}
}
void InefficientStringConcatenationCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *LhsStr = Result.Nodes.getNodeAs<DeclRefExpr>("lhsStr");
const auto *PlusOperator =
Result.Nodes.getNodeAs<CXXOperatorCallExpr>("plusOperator");
const auto DiagMsg =
"string concatenation results in allocation of unnecessary temporary "
"strings; consider using 'operator+=' or 'string::append()' instead";
if (LhsStr)
diag(LhsStr->getExprLoc(), DiagMsg);
else if (PlusOperator)
diag(PlusOperator->getExprLoc(), DiagMsg);
}
} // namespace performance
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,41 @@
//===--- InefficientStringConcatenationCheck.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_PERFORMANCE_INEFFICIENTSTRINGCONCATENATION_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_INEFFICIENTSTRINGCONCATENATION_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace performance {
/// This check is to warn about the performance overhead arising from
/// concatenating strings, using the operator+, instead of operator+=.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/performance-inefficient-string-concatenation.html
class InefficientStringConcatenationCheck : public ClangTidyCheck {
public:
InefficientStringConcatenationCheck(StringRef Name,
ClangTidyContext *Context);
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
private:
const bool StrictMode;
};
} // namespace performance
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_INEFFICIENTSTRINGCONCATENATION_H

View File

@@ -0,0 +1,214 @@
//===--- InefficientVectorOperationCheck.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 "InefficientVectorOperationCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
#include "../utils/DeclRefExprUtils.h"
#include "../utils/OptionsUtils.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace performance {
namespace {
// Matcher names. Given the code:
//
// \code
// void f() {
// vector<T> v;
// for (int i = 0; i < 10 + 1; ++i) {
// v.push_back(i);
// }
// }
// \endcode
//
// The matcher names are bound to following parts of the AST:
// - LoopCounterName: The entire for loop (as ForStmt).
// - LoopParentName: The body of function f (as CompoundStmt).
// - VectorVarDeclName: 'v' in (as VarDecl).
// - VectorVarDeclStmatName: The entire 'std::vector<T> v;' statement (as
// DeclStmt).
// - PushBackOrEmplaceBackCallName: 'v.push_back(i)' (as cxxMemberCallExpr).
// - LoopInitVarName: 'i' (as VarDecl).
// - LoopEndExpr: '10+1' (as Expr).
static const char LoopCounterName[] = "for_loop_counter";
static const char LoopParentName[] = "loop_parent";
static const char VectorVarDeclName[] = "vector_var_decl";
static const char VectorVarDeclStmtName[] = "vector_var_decl_stmt";
static const char PushBackOrEmplaceBackCallName[] = "append_call";
static const char LoopInitVarName[] = "loop_init_var";
static const char LoopEndExprName[] = "loop_end_expr";
static const char RangeLoopName[] = "for_range_loop";
ast_matchers::internal::Matcher<Expr> supportedContainerTypesMatcher() {
return hasType(cxxRecordDecl(hasAnyName(
"::std::vector", "::std::set", "::std::unordered_set", "::std::map",
"::std::unordered_map", "::std::array", "::std::deque")));
}
} // namespace
InefficientVectorOperationCheck::InefficientVectorOperationCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
VectorLikeClasses(utils::options::parseStringList(
Options.get("VectorLikeClasses", "::std::vector"))) {}
void InefficientVectorOperationCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "VectorLikeClasses",
utils::options::serializeStringList(VectorLikeClasses));
}
void InefficientVectorOperationCheck::registerMatchers(MatchFinder *Finder) {
const auto VectorDecl = cxxRecordDecl(hasAnyName(SmallVector<StringRef, 5>(
VectorLikeClasses.begin(), VectorLikeClasses.end())));
const auto VectorDefaultConstructorCall = cxxConstructExpr(
hasType(VectorDecl),
hasDeclaration(cxxConstructorDecl(isDefaultConstructor())));
const auto VectorVarDecl =
varDecl(hasInitializer(VectorDefaultConstructorCall))
.bind(VectorVarDeclName);
const auto VectorAppendCallExpr =
cxxMemberCallExpr(
callee(cxxMethodDecl(hasAnyName("push_back", "emplace_back"))),
on(hasType(VectorDecl)),
onImplicitObjectArgument(declRefExpr(to(VectorVarDecl))))
.bind(PushBackOrEmplaceBackCallName);
const auto VectorAppendCall = expr(ignoringImplicit(VectorAppendCallExpr));
const auto VectorVarDefStmt =
declStmt(hasSingleDecl(equalsBoundNode(VectorVarDeclName)))
.bind(VectorVarDeclStmtName);
const auto LoopVarInit =
declStmt(hasSingleDecl(varDecl(hasInitializer(integerLiteral(equals(0))))
.bind(LoopInitVarName)));
const auto RefersToLoopVar = ignoringParenImpCasts(
declRefExpr(to(varDecl(equalsBoundNode(LoopInitVarName)))));
// Matchers for the loop whose body has only 1 push_back/emplace_back calling
// statement.
const auto HasInterestingLoopBody =
hasBody(anyOf(compoundStmt(statementCountIs(1), has(VectorAppendCall)),
VectorAppendCall));
const auto InInterestingCompoundStmt =
hasParent(compoundStmt(has(VectorVarDefStmt)).bind(LoopParentName));
// Match counter-based for loops:
// for (int i = 0; i < n; ++i) { v.push_back(...); }
//
// FIXME: Support more types of counter-based loops like decrement loops.
Finder->addMatcher(
forStmt(
hasLoopInit(LoopVarInit),
hasCondition(binaryOperator(
hasOperatorName("<"), hasLHS(RefersToLoopVar),
hasRHS(expr(unless(hasDescendant(expr(RefersToLoopVar))))
.bind(LoopEndExprName)))),
hasIncrement(unaryOperator(hasOperatorName("++"),
hasUnaryOperand(RefersToLoopVar))),
HasInterestingLoopBody, InInterestingCompoundStmt)
.bind(LoopCounterName),
this);
// Match for-range loops:
// for (const auto& E : data) { v.push_back(...); }
//
// FIXME: Support more complex range-expressions.
Finder->addMatcher(
cxxForRangeStmt(
hasRangeInit(declRefExpr(supportedContainerTypesMatcher())),
HasInterestingLoopBody, InInterestingCompoundStmt)
.bind(RangeLoopName),
this);
}
void InefficientVectorOperationCheck::check(
const MatchFinder::MatchResult &Result) {
auto* Context = Result.Context;
if (Context->getDiagnostics().hasUncompilableErrorOccurred())
return;
const SourceManager &SM = *Result.SourceManager;
const auto *VectorVarDecl =
Result.Nodes.getNodeAs<VarDecl>(VectorVarDeclName);
const auto *ForLoop = Result.Nodes.getNodeAs<ForStmt>(LoopCounterName);
const auto *RangeLoop =
Result.Nodes.getNodeAs<CXXForRangeStmt>(RangeLoopName);
const auto *VectorAppendCall =
Result.Nodes.getNodeAs<CXXMemberCallExpr>(PushBackOrEmplaceBackCallName);
const auto *LoopEndExpr = Result.Nodes.getNodeAs<Expr>(LoopEndExprName);
const auto *LoopParent = Result.Nodes.getNodeAs<CompoundStmt>(LoopParentName);
const Stmt *LoopStmt = ForLoop;
if (!LoopStmt)
LoopStmt = RangeLoop;
llvm::SmallPtrSet<const DeclRefExpr *, 16> AllVectorVarRefs =
utils::decl_ref_expr::allDeclRefExprs(*VectorVarDecl, *LoopParent,
*Context);
for (const auto *Ref : AllVectorVarRefs) {
// Skip cases where there are usages (defined as DeclRefExpr that refers to
// "v") of vector variable `v` before the for loop. We consider these usages
// are operations causing memory preallocation (e.g. "v.resize(n)",
// "v.reserve(n)").
//
// FIXME: make it more intelligent to identify the pre-allocating operations
// before the for loop.
if (SM.isBeforeInTranslationUnit(Ref->getLocation(),
LoopStmt->getLocStart())) {
return;
}
}
llvm::StringRef VectorVarName = Lexer::getSourceText(
CharSourceRange::getTokenRange(
VectorAppendCall->getImplicitObjectArgument()->getSourceRange()),
SM, Context->getLangOpts());
std::string ReserveStmt;
// Handle for-range loop cases.
if (RangeLoop) {
// Get the range-expression in a for-range statement represented as
// `for (range-declarator: range-expression)`.
StringRef RangeInitExpName = Lexer::getSourceText(
CharSourceRange::getTokenRange(
RangeLoop->getRangeInit()->getSourceRange()),
SM, Context->getLangOpts());
ReserveStmt =
(VectorVarName + ".reserve(" + RangeInitExpName + ".size()" + ");\n")
.str();
} else if (ForLoop) {
// Handle counter-based loop cases.
StringRef LoopEndSource = Lexer::getSourceText(
CharSourceRange::getTokenRange(LoopEndExpr->getSourceRange()), SM,
Context->getLangOpts());
ReserveStmt = (VectorVarName + ".reserve(" + LoopEndSource + ");\n").str();
}
auto Diag =
diag(VectorAppendCall->getLocStart(),
"%0 is called inside a loop; "
"consider pre-allocating the vector capacity before the loop")
<< VectorAppendCall->getMethodDecl()->getDeclName();
if (!ReserveStmt.empty())
Diag << FixItHint::CreateInsertion(LoopStmt->getLocStart(), ReserveStmt);
}
} // namespace performance
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,39 @@
//===--- InefficientVectorOperationCheck.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_PERFORMANCE_INEFFICIENT_VECTOR_OPERATION_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_INEFFICIENT_VECTOR_OPERATION_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace performance {
/// Finds possible inefficient `std::vector` operations (e.g. `push_back`) in
/// for loops that may cause unnecessary memory reallocations.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/performance-inefficient-vector-operation.html
class InefficientVectorOperationCheck : public ClangTidyCheck {
public:
InefficientVectorOperationCheck(StringRef Name, ClangTidyContext *Context);
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
private:
const std::vector<std::string> VectorLikeClasses;
};
} // namespace performance
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_INEFFICIENT_VECTOR_OPERATION_H

View File

@@ -0,0 +1,121 @@
//===--- MoveConstArgCheck.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 "MoveConstArgCheck.h"
#include "clang/Lex/Lexer.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace performance {
static void ReplaceCallWithArg(const CallExpr *Call, DiagnosticBuilder &Diag,
const SourceManager &SM,
const LangOptions &LangOpts) {
const Expr *Arg = Call->getArg(0);
CharSourceRange BeforeArgumentsRange = Lexer::makeFileCharRange(
CharSourceRange::getCharRange(Call->getLocStart(), Arg->getLocStart()),
SM, LangOpts);
CharSourceRange AfterArgumentsRange = Lexer::makeFileCharRange(
CharSourceRange::getCharRange(Call->getLocEnd(),
Call->getLocEnd().getLocWithOffset(1)),
SM, LangOpts);
if (BeforeArgumentsRange.isValid() && AfterArgumentsRange.isValid()) {
Diag << FixItHint::CreateRemoval(BeforeArgumentsRange)
<< FixItHint::CreateRemoval(AfterArgumentsRange);
}
}
void MoveConstArgCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "CheckTriviallyCopyableMove", CheckTriviallyCopyableMove);
}
void MoveConstArgCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)
return;
auto MoveCallMatcher =
callExpr(callee(functionDecl(hasName("::std::move"))), argumentCountIs(1),
unless(isInTemplateInstantiation()))
.bind("call-move");
Finder->addMatcher(MoveCallMatcher, this);
auto ConstParamMatcher = forEachArgumentWithParam(
MoveCallMatcher, parmVarDecl(hasType(references(isConstQualified()))));
Finder->addMatcher(callExpr(ConstParamMatcher).bind("receiving-expr"), this);
Finder->addMatcher(cxxConstructExpr(ConstParamMatcher).bind("receiving-expr"),
this);
}
void MoveConstArgCheck::check(const MatchFinder::MatchResult &Result) {
const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move");
const auto *ReceivingExpr = Result.Nodes.getNodeAs<Expr>("receiving-expr");
const Expr *Arg = CallMove->getArg(0);
SourceManager &SM = Result.Context->getSourceManager();
CharSourceRange MoveRange =
CharSourceRange::getCharRange(CallMove->getSourceRange());
CharSourceRange FileMoveRange =
Lexer::makeFileCharRange(MoveRange, SM, getLangOpts());
if (!FileMoveRange.isValid())
return;
bool IsConstArg = Arg->getType().isConstQualified();
bool IsTriviallyCopyable =
Arg->getType().isTriviallyCopyableType(*Result.Context);
if (IsConstArg || IsTriviallyCopyable) {
if (const CXXRecordDecl *R = Arg->getType()->getAsCXXRecordDecl()) {
// According to [expr.prim.lambda]p3, "whether the closure type is
// trivially copyable" property can be changed by the implementation of
// the language, so we shouldn't rely on it when issuing diagnostics.
if (R->isLambda())
return;
// Don't warn when the type is not copyable.
for (const auto *Ctor : R->ctors()) {
if (Ctor->isCopyConstructor() && Ctor->isDeleted())
return;
}
}
if (!IsConstArg && IsTriviallyCopyable && !CheckTriviallyCopyableMove)
return;
bool IsVariable = isa<DeclRefExpr>(Arg);
const auto *Var =
IsVariable ? dyn_cast<DeclRefExpr>(Arg)->getDecl() : nullptr;
auto Diag = diag(FileMoveRange.getBegin(),
"std::move of the %select{|const }0"
"%select{expression|variable %4}1 "
"%select{|of the trivially-copyable type %5 }2"
"has no effect; remove std::move()"
"%select{| or make the variable non-const}3")
<< IsConstArg << IsVariable << IsTriviallyCopyable
<< (IsConstArg && IsVariable && !IsTriviallyCopyable) << Var
<< Arg->getType();
ReplaceCallWithArg(CallMove, Diag, SM, getLangOpts());
} else if (ReceivingExpr) {
auto Diag = diag(FileMoveRange.getBegin(),
"passing result of std::move() as a const reference "
"argument; no move will actually happen");
ReplaceCallWithArg(CallMove, Diag, SM, getLangOpts());
}
}
} // namespace performance
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,43 @@
//===--- MoveConstArgCheck.h - clang-tidy -------------------------===//
//
// 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_MISC_MOVECONSTANTARGUMENTCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MOVECONSTANTARGUMENTCHECK_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace performance {
/// Find casts of calculation results to bigger type. Typically from int to
///
/// There is one option:
///
/// - `CheckTriviallyCopyableMove`: Whether to check for trivially-copyable
// types as their objects are not moved but copied. Enabled by default.
class MoveConstArgCheck : public ClangTidyCheck {
public:
MoveConstArgCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
CheckTriviallyCopyableMove(
Options.get("CheckTriviallyCopyableMove", true)) {}
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
const bool CheckTriviallyCopyableMove;
};
} // namespace performance
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MOVECONSTANTARGUMENTCHECK_H

View File

@@ -0,0 +1,110 @@
//===--- MoveConstructorInitCheck.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 "MoveConstructorInitCheck.h"
#include "../utils/Matchers.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/Preprocessor.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace performance {
MoveConstructorInitCheck::MoveConstructorInitCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
Options.getLocalOrGlobal("IncludeStyle", "llvm"))) {}
void MoveConstructorInitCheck::registerMatchers(MatchFinder *Finder) {
// Only register the matchers for C++11; the functionality currently does not
// provide any benefit to other languages, despite being benign.
if (!getLangOpts().CPlusPlus11)
return;
Finder->addMatcher(
cxxConstructorDecl(
unless(isImplicit()),
allOf(isMoveConstructor(),
hasAnyConstructorInitializer(
cxxCtorInitializer(
withInitializer(cxxConstructExpr(hasDeclaration(
cxxConstructorDecl(isCopyConstructor())
.bind("ctor")))))
.bind("move-init")))),
this);
}
void MoveConstructorInitCheck::check(const MatchFinder::MatchResult &Result) {
const auto *CopyCtor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
const auto *Initializer =
Result.Nodes.getNodeAs<CXXCtorInitializer>("move-init");
// Do not diagnose if the expression used to perform the initialization is a
// trivially-copyable type.
QualType QT = Initializer->getInit()->getType();
if (QT.isTriviallyCopyableType(*Result.Context))
return;
if (QT.isConstQualified())
return;
const auto *RD = QT->getAsCXXRecordDecl();
if (RD && RD->isTriviallyCopyable())
return;
// Diagnose when the class type has a move constructor available, but the
// ctor-initializer uses the copy constructor instead.
const CXXConstructorDecl *Candidate = nullptr;
for (const auto *Ctor : CopyCtor->getParent()->ctors()) {
if (Ctor->isMoveConstructor() && Ctor->getAccess() <= AS_protected &&
!Ctor->isDeleted()) {
// The type has a move constructor that is at least accessible to the
// initializer.
//
// FIXME: Determine whether the move constructor is a viable candidate
// for the ctor-initializer, perhaps provide a fixit that suggests
// using std::move().
Candidate = Ctor;
break;
}
}
if (Candidate) {
// There's a move constructor candidate that the caller probably intended
// to call instead.
diag(Initializer->getSourceLocation(),
"move constructor initializes %0 by calling a copy constructor")
<< (Initializer->isBaseInitializer() ? "base class" : "class member");
diag(CopyCtor->getLocation(), "copy constructor being called",
DiagnosticIDs::Note);
diag(Candidate->getLocation(), "candidate move constructor here",
DiagnosticIDs::Note);
}
}
void MoveConstructorInitCheck::registerPPCallbacks(CompilerInstance &Compiler) {
Inserter.reset(new utils::IncludeInserter(
Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
}
void MoveConstructorInitCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IncludeStyle",
utils::IncludeSorter::toString(IncludeStyle));
}
} // namespace performance
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,44 @@
//===--- MoveConstructorInitCheck.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_PERFORMANCE_MOVECONSTRUCTORINITCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_MOVECONSTRUCTORINITCHECK_H
#include "../ClangTidy.h"
#include "../utils/IncludeInserter.h"
#include <memory>
namespace clang {
namespace tidy {
namespace performance {
/// The check flags user-defined move constructors that have a ctor-initializer
/// initializing a member or base class through a copy constructor instead of a
/// move constructor.
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/performance-move-constructor-init.html
class MoveConstructorInitCheck : public ClangTidyCheck {
public:
MoveConstructorInitCheck(StringRef Name, ClangTidyContext *Context);
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void registerPPCallbacks(clang::CompilerInstance &Compiler) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
private:
std::unique_ptr<utils::IncludeInserter> Inserter;
const utils::IncludeSorter::IncludeStyle IncludeStyle;
};
} // namespace performance
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_MOVECONSTRUCTORINITCHECK_H

View File

@@ -0,0 +1,77 @@
//===--- NoexceptMoveConstructorCheck.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 "NoexceptMoveConstructorCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace performance {
void NoexceptMoveConstructorCheck::registerMatchers(MatchFinder *Finder) {
// Only register the matchers for C++11; the functionality currently does not
// provide any benefit to other languages, despite being benign.
if (!getLangOpts().CPlusPlus11)
return;
Finder->addMatcher(
cxxMethodDecl(anyOf(cxxConstructorDecl(), hasOverloadedOperatorName("=")),
unless(isImplicit()), unless(isDeleted()))
.bind("decl"),
this);
}
void NoexceptMoveConstructorCheck::check(
const MatchFinder::MatchResult &Result) {
if (const auto *Decl = Result.Nodes.getNodeAs<CXXMethodDecl>("decl")) {
StringRef MethodType = "assignment operator";
if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Decl)) {
if (!Ctor->isMoveConstructor())
return;
MethodType = "constructor";
} else if (!Decl->isMoveAssignmentOperator()) {
return;
}
const auto *ProtoType = Decl->getType()->getAs<FunctionProtoType>();
if (isUnresolvedExceptionSpec(ProtoType->getExceptionSpecType()))
return;
switch (ProtoType->getNoexceptSpec(*Result.Context)) {
case FunctionProtoType::NR_NoNoexcept:
diag(Decl->getLocation(), "move %0s should be marked noexcept")
<< MethodType;
// FIXME: Add a fixit.
break;
case FunctionProtoType::NR_Throw:
// Don't complain about nothrow(false), but complain on nothrow(expr)
// where expr evaluates to false.
if (const Expr *E = ProtoType->getNoexceptExpr()) {
if (isa<CXXBoolLiteralExpr>(E))
break;
diag(E->getExprLoc(),
"noexcept specifier on the move %0 evaluates to 'false'")
<< MethodType;
}
break;
case FunctionProtoType::NR_Nothrow:
case FunctionProtoType::NR_Dependent:
case FunctionProtoType::NR_BadNoexcept:
break;
}
}
}
} // namespace performance
} // namespace tidy
} // namespace clang

View File

@@ -0,0 +1,38 @@
//===--- NoexceptMoveConstructorCheck.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_PERFORMANCE_NOEXCEPTMOVECONSTRUCTORCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_NOEXCEPTMOVECONSTRUCTORCHECK_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace performance {
/// The check flags user-defined move constructors and assignment operators not
/// marked with `noexcept` or marked with `noexcept(expr)` where `expr`
/// evaluates to `false` (but is not a `false` literal itself).
///
/// Move constructors of all the types used with STL containers, for example,
/// need to be declared `noexcept`. Otherwise STL will choose copy constructors
/// instead. The same is valid for move assignment operations.
class NoexceptMoveConstructorCheck : public ClangTidyCheck {
public:
NoexceptMoveConstructorCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace performance
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_NOEXCEPTMOVECONSTRUCTORCHECK_H

View File

@@ -0,0 +1,71 @@
//===--- PeformanceTidyModule.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 "FasterStringFindCheck.h"
#include "ForRangeCopyCheck.h"
#include "ImplicitConversionInLoopCheck.h"
#include "InefficientAlgorithmCheck.h"
#include "InefficientStringConcatenationCheck.h"
#include "InefficientVectorOperationCheck.h"
#include "MoveConstArgCheck.h"
#include "MoveConstructorInitCheck.h"
#include "NoexceptMoveConstructorCheck.h"
#include "TypePromotionInMathFnCheck.h"
#include "UnnecessaryCopyInitialization.h"
#include "UnnecessaryValueParamCheck.h"
namespace clang {
namespace tidy {
namespace performance {
class PerformanceModule : public ClangTidyModule {
public:
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
CheckFactories.registerCheck<FasterStringFindCheck>(
"performance-faster-string-find");
CheckFactories.registerCheck<ForRangeCopyCheck>(
"performance-for-range-copy");
CheckFactories.registerCheck<ImplicitConversionInLoopCheck>(
"performance-implicit-conversion-in-loop");
CheckFactories.registerCheck<InefficientAlgorithmCheck>(
"performance-inefficient-algorithm");
CheckFactories.registerCheck<InefficientStringConcatenationCheck>(
"performance-inefficient-string-concatenation");
CheckFactories.registerCheck<InefficientVectorOperationCheck>(
"performance-inefficient-vector-operation");
CheckFactories.registerCheck<MoveConstArgCheck>(
"performance-move-const-arg");
CheckFactories.registerCheck<MoveConstructorInitCheck>(
"performance-move-constructor-init");
CheckFactories.registerCheck<NoexceptMoveConstructorCheck>(
"performance-noexcept-move-constructor");
CheckFactories.registerCheck<TypePromotionInMathFnCheck>(
"performance-type-promotion-in-math-fn");
CheckFactories.registerCheck<UnnecessaryCopyInitialization>(
"performance-unnecessary-copy-initialization");
CheckFactories.registerCheck<UnnecessaryValueParamCheck>(
"performance-unnecessary-value-param");
}
};
// Register the PerformanceModule using this statically initialized variable.
static ClangTidyModuleRegistry::Add<PerformanceModule>
X("performance-module", "Adds performance checks.");
} // namespace performance
// This anchor is used to force the linker to link in the generated object file
// and thus register the PerformanceModule.
volatile int PerformanceModuleAnchorSource = 0;
} // namespace tidy
} // namespace clang

Some files were not shown because too many files have changed in this diff Show More