[clang-tidy] Move a few more checks from misc to bugprone.
Summary:
clang_tidy/rename_check.py misc-assert-side-effect bugprone-assert-side-effect
clang_tidy/rename_check.py misc-bool-pointer-implicit-conversion bugprone-bool-pointer-implicit-conversion
clang_tidy/rename_check.py misc-fold-init-type bugprone-fold-init-type
clang_tidy/rename_check.py misc-forward-declaration-namespace bugprone-forward-declaration-namespace
clang_tidy/rename_check.py misc-inaccurate-erase bugprone-inaccurate-erase
clang_tidy/rename_check.py misc-move-forwarding-reference bugprone-move-forwarding-reference
clang_tidy/rename_check.py misc-multiple-statement-macro bugprone-multiple-statement-macro
clang_tidy/rename_check.py misc-use-after-move bugprone-use-after-move
clang_tidy/rename_check.py misc-virtual-near-miss bugprone-virtual-near-miss
Manually fixed a reference to UseAfterMoveCheck in the hicpp module.
Manually fixed header guards.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nemanjai, mgorny, javed.absar, xazax.hun, kbarton, cfe-commits
Differential Revision: https://reviews.llvm.org/D40426
llvm-svn: 318950
diff --git a/clang-tools-extra/clang-tidy/bugprone/ArgumentCommentCheck.h b/clang-tools-extra/clang-tidy/bugprone/ArgumentCommentCheck.h
index 050780d..2f5a751 100644
--- a/clang-tools-extra/clang-tidy/bugprone/ArgumentCommentCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/ArgumentCommentCheck.h
@@ -7,8 +7,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ARGUMENTCOMMENTCHECK_H
-#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ARGUMENTCOMMENTCHECK_H
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_ARGUMENTCOMMENTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_ARGUMENTCOMMENTCHECK_H
#include "../ClangTidy.h"
#include "llvm/Support/Regex.h"
@@ -52,4 +52,4 @@
} // namespace tidy
} // namespace clang
-#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ARGUMENTCOMMENTCHECK_H
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_ARGUMENTCOMMENTCHECK_H
diff --git a/clang-tools-extra/clang-tidy/bugprone/AssertSideEffectCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/AssertSideEffectCheck.cpp
new file mode 100644
index 0000000..244e755
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/AssertSideEffectCheck.cpp
@@ -0,0 +1,127 @@
+//===--- AssertSideEffectCheck.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 "AssertSideEffectCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include <algorithm>
+#include <string>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+namespace {
+
+AST_MATCHER_P(Expr, hasSideEffect, bool, CheckFunctionCalls) {
+ const Expr *E = &Node;
+
+ if (const auto *Op = dyn_cast<UnaryOperator>(E)) {
+ UnaryOperator::Opcode OC = Op->getOpcode();
+ return OC == UO_PostInc || OC == UO_PostDec || OC == UO_PreInc ||
+ OC == UO_PreDec;
+ }
+
+ if (const auto *Op = dyn_cast<BinaryOperator>(E)) {
+ return Op->isAssignmentOp();
+ }
+
+ if (const auto *OpCallExpr = dyn_cast<CXXOperatorCallExpr>(E)) {
+ OverloadedOperatorKind OpKind = OpCallExpr->getOperator();
+ return OpKind == OO_Equal || OpKind == OO_PlusEqual ||
+ OpKind == OO_MinusEqual || OpKind == OO_StarEqual ||
+ OpKind == OO_SlashEqual || OpKind == OO_AmpEqual ||
+ OpKind == OO_PipeEqual || OpKind == OO_CaretEqual ||
+ OpKind == OO_LessLessEqual || OpKind == OO_GreaterGreaterEqual ||
+ OpKind == OO_PlusPlus || OpKind == OO_MinusMinus ||
+ OpKind == OO_PercentEqual || OpKind == OO_New ||
+ OpKind == OO_Delete || OpKind == OO_Array_New ||
+ OpKind == OO_Array_Delete;
+ }
+
+ if (const auto *CExpr = dyn_cast<CallExpr>(E)) {
+ bool Result = CheckFunctionCalls;
+ if (const auto *FuncDecl = CExpr->getDirectCallee()) {
+ if (FuncDecl->getDeclName().isIdentifier() &&
+ FuncDecl->getName() == "__builtin_expect") // exceptions come here
+ Result = false;
+ else if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FuncDecl))
+ Result &= !MethodDecl->isConst();
+ }
+ return Result;
+ }
+
+ return isa<CXXNewExpr>(E) || isa<CXXDeleteExpr>(E) || isa<CXXThrowExpr>(E);
+}
+
+} // namespace
+
+AssertSideEffectCheck::AssertSideEffectCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ CheckFunctionCalls(Options.get("CheckFunctionCalls", false)),
+ RawAssertList(Options.get("AssertMacros", "assert")) {
+ StringRef(RawAssertList).split(AssertMacros, ",", -1, false);
+}
+
+// The options are explained in AssertSideEffectCheck.h.
+void AssertSideEffectCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "CheckFunctionCalls", CheckFunctionCalls);
+ Options.store(Opts, "AssertMacros", RawAssertList);
+}
+
+void AssertSideEffectCheck::registerMatchers(MatchFinder *Finder) {
+ auto DescendantWithSideEffect =
+ hasDescendant(expr(hasSideEffect(CheckFunctionCalls)));
+ auto ConditionWithSideEffect = hasCondition(DescendantWithSideEffect);
+ Finder->addMatcher(
+ stmt(
+ anyOf(conditionalOperator(ConditionWithSideEffect),
+ ifStmt(ConditionWithSideEffect),
+ unaryOperator(hasOperatorName("!"),
+ hasUnaryOperand(unaryOperator(
+ hasOperatorName("!"),
+ hasUnaryOperand(DescendantWithSideEffect))))))
+ .bind("condStmt"),
+ this);
+}
+
+void AssertSideEffectCheck::check(const MatchFinder::MatchResult &Result) {
+ const SourceManager &SM = *Result.SourceManager;
+ const LangOptions LangOpts = getLangOpts();
+ SourceLocation Loc = Result.Nodes.getNodeAs<Stmt>("condStmt")->getLocStart();
+
+ StringRef AssertMacroName;
+ while (Loc.isValid() && Loc.isMacroID()) {
+ StringRef MacroName = Lexer::getImmediateMacroName(Loc, SM, LangOpts);
+
+ // Check if this macro is an assert.
+ if (std::find(AssertMacros.begin(), AssertMacros.end(), MacroName) !=
+ AssertMacros.end()) {
+ AssertMacroName = MacroName;
+ break;
+ }
+ Loc = SM.getImmediateMacroCallerLoc(Loc);
+ }
+ if (AssertMacroName.empty())
+ return;
+
+ diag(Loc, "found %0() with side effect") << AssertMacroName;
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tools-extra/clang-tidy/bugprone/AssertSideEffectCheck.h b/clang-tools-extra/clang-tidy/bugprone/AssertSideEffectCheck.h
new file mode 100644
index 0000000..0f386c9
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/AssertSideEffectCheck.h
@@ -0,0 +1,52 @@
+//===--- AssertSideEffectCheck.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_BUGPRONE_ASSERTSIDEEFFECTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_ASSERTSIDEEFFECTCHECK_H
+
+#include "../ClangTidy.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// Finds `assert()` with side effect.
+///
+/// The condition of `assert()` is evaluated only in debug builds so a
+/// condition with side effect can cause different behavior in debug / release
+/// builds.
+///
+/// There are two options:
+///
+/// - `AssertMacros`: A comma-separated list of the names of assert macros to
+/// be checked.
+/// - `CheckFunctionCalls`: Whether to treat non-const member and non-member
+/// functions as they produce side effects. Disabled by default because it
+/// can increase the number of false positive warnings.
+class AssertSideEffectCheck : public ClangTidyCheck {
+public:
+ AssertSideEffectCheck(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:
+ const bool CheckFunctionCalls;
+ const std::string RawAssertList;
+ SmallVector<StringRef, 5> AssertMacros;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_ASSERTSIDEEFFECTCHECK_H
diff --git a/clang-tools-extra/clang-tidy/bugprone/BoolPointerImplicitConversionCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/BoolPointerImplicitConversionCheck.cpp
new file mode 100644
index 0000000..ed2c2db
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/BoolPointerImplicitConversionCheck.cpp
@@ -0,0 +1,73 @@
+//===--- BoolPointerImplicitConversionCheck.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 "BoolPointerImplicitConversionCheck.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+void BoolPointerImplicitConversionCheck::registerMatchers(MatchFinder *Finder) {
+ // Look for ifs that have an implicit bool* to bool conversion in the
+ // condition. Filter negations.
+ Finder->addMatcher(
+ ifStmt(hasCondition(findAll(implicitCastExpr(
+ allOf(unless(hasParent(unaryOperator(hasOperatorName("!")))),
+ hasSourceExpression(expr(
+ hasType(pointerType(pointee(booleanType()))),
+ ignoringParenImpCasts(declRefExpr().bind("expr")))),
+ hasCastKind(CK_PointerToBoolean))))),
+ unless(isInTemplateInstantiation()))
+ .bind("if"),
+ this);
+}
+
+void BoolPointerImplicitConversionCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ auto *If = Result.Nodes.getNodeAs<IfStmt>("if");
+ auto *Var = Result.Nodes.getNodeAs<DeclRefExpr>("expr");
+
+ // Ignore macros.
+ if (Var->getLocStart().isMacroID())
+ return;
+
+ // Only allow variable accesses for now, no function calls or member exprs.
+ // Check that we don't dereference the variable anywhere within the if. This
+ // avoids false positives for checks of the pointer for nullptr before it is
+ // dereferenced. If there is a dereferencing operator on this variable don't
+ // emit a diagnostic. Also ignore array subscripts.
+ const Decl *D = Var->getDecl();
+ auto DeclRef = ignoringParenImpCasts(declRefExpr(to(equalsNode(D))));
+ if (!match(findAll(
+ unaryOperator(hasOperatorName("*"), hasUnaryOperand(DeclRef))),
+ *If, *Result.Context)
+ .empty() ||
+ !match(findAll(arraySubscriptExpr(hasBase(DeclRef))), *If,
+ *Result.Context)
+ .empty() ||
+ // FIXME: We should still warn if the paremater is implicitly converted to
+ // bool.
+ !match(findAll(callExpr(hasAnyArgument(ignoringParenImpCasts(DeclRef)))),
+ *If, *Result.Context)
+ .empty() ||
+ !match(findAll(cxxDeleteExpr(has(ignoringParenImpCasts(expr(DeclRef))))),
+ *If, *Result.Context)
+ .empty())
+ return;
+
+ diag(Var->getLocStart(), "dubious check of 'bool *' against 'nullptr', did "
+ "you mean to dereference it?")
+ << FixItHint::CreateInsertion(Var->getLocStart(), "*");
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tools-extra/clang-tidy/bugprone/BoolPointerImplicitConversionCheck.h b/clang-tools-extra/clang-tidy/bugprone/BoolPointerImplicitConversionCheck.h
new file mode 100644
index 0000000..b3416a9
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/BoolPointerImplicitConversionCheck.h
@@ -0,0 +1,42 @@
+//===--- BoolPointerImplicitConversionCheck.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_BUGPRONE_BOOLPOINTERIMPLICITCONVERSIONCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_BOOLPOINTERIMPLICITCONVERSIONCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// Checks for conditions based on implicit conversion from a bool pointer to
+/// bool.
+///
+/// Example:
+///
+/// \code
+/// bool *p;
+/// if (p) {
+/// // Never used in a pointer-specific way.
+/// }
+/// \endcode
+class BoolPointerImplicitConversionCheck : public ClangTidyCheck {
+public:
+ BoolPointerImplicitConversionCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_BOOLPOINTERIMPLICITCONVERSIONCHECK_H
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index e96dc81..d17eb27 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -11,13 +11,22 @@
#include "../ClangTidyModule.h"
#include "../ClangTidyModuleRegistry.h"
#include "ArgumentCommentCheck.h"
+#include "AssertSideEffectCheck.h"
+#include "BoolPointerImplicitConversionCheck.h"
#include "CopyConstructorInitCheck.h"
#include "DanglingHandleCheck.h"
+#include "FoldInitTypeCheck.h"
+#include "ForwardDeclarationNamespaceCheck.h"
+#include "InaccurateEraseCheck.h"
#include "IntegerDivisionCheck.h"
#include "MisplacedOperatorInStrlenInAllocCheck.h"
+#include "MoveForwardingReferenceCheck.h"
+#include "MultipleStatementMacroCheck.h"
#include "StringConstructorCheck.h"
#include "SuspiciousMemsetUsageCheck.h"
#include "UndefinedMemoryManipulationCheck.h"
+#include "UseAfterMoveCheck.h"
+#include "VirtualNearMissCheck.h"
namespace clang {
namespace tidy {
@@ -28,20 +37,38 @@
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
CheckFactories.registerCheck<ArgumentCommentCheck>(
"bugprone-argument-comment");
+ CheckFactories.registerCheck<AssertSideEffectCheck>(
+ "bugprone-assert-side-effect");
+ CheckFactories.registerCheck<BoolPointerImplicitConversionCheck>(
+ "bugprone-bool-pointer-implicit-conversion");
CheckFactories.registerCheck<CopyConstructorInitCheck>(
"bugprone-copy-constructor-init");
CheckFactories.registerCheck<DanglingHandleCheck>(
"bugprone-dangling-handle");
+ CheckFactories.registerCheck<FoldInitTypeCheck>(
+ "bugprone-fold-init-type");
+ CheckFactories.registerCheck<ForwardDeclarationNamespaceCheck>(
+ "bugprone-forward-declaration-namespace");
+ CheckFactories.registerCheck<InaccurateEraseCheck>(
+ "bugprone-inaccurate-erase");
CheckFactories.registerCheck<IntegerDivisionCheck>(
"bugprone-integer-division");
CheckFactories.registerCheck<MisplacedOperatorInStrlenInAllocCheck>(
"bugprone-misplaced-operator-in-strlen-in-alloc");
+ CheckFactories.registerCheck<MoveForwardingReferenceCheck>(
+ "bugprone-move-forwarding-reference");
+ CheckFactories.registerCheck<MultipleStatementMacroCheck>(
+ "bugprone-multiple-statement-macro");
CheckFactories.registerCheck<StringConstructorCheck>(
"bugprone-string-constructor");
CheckFactories.registerCheck<SuspiciousMemsetUsageCheck>(
"bugprone-suspicious-memset-usage");
CheckFactories.registerCheck<UndefinedMemoryManipulationCheck>(
"bugprone-undefined-memory-manipulation");
+ CheckFactories.registerCheck<UseAfterMoveCheck>(
+ "bugprone-use-after-move");
+ CheckFactories.registerCheck<VirtualNearMissCheck>(
+ "bugprone-virtual-near-miss");
}
};
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index fe9b999..dab77bd 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -2,14 +2,23 @@
add_clang_library(clangTidyBugproneModule
ArgumentCommentCheck.cpp
+ AssertSideEffectCheck.cpp
+ BoolPointerImplicitConversionCheck.cpp
BugproneTidyModule.cpp
CopyConstructorInitCheck.cpp
DanglingHandleCheck.cpp
+ FoldInitTypeCheck.cpp
+ ForwardDeclarationNamespaceCheck.cpp
+ InaccurateEraseCheck.cpp
IntegerDivisionCheck.cpp
MisplacedOperatorInStrlenInAllocCheck.cpp
+ MoveForwardingReferenceCheck.cpp
+ MultipleStatementMacroCheck.cpp
StringConstructorCheck.cpp
SuspiciousMemsetUsageCheck.cpp
UndefinedMemoryManipulationCheck.cpp
+ UseAfterMoveCheck.cpp
+ VirtualNearMissCheck.cpp
LINK_LIBS
clangAnalysis
diff --git a/clang-tools-extra/clang-tidy/bugprone/FoldInitTypeCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/FoldInitTypeCheck.cpp
new file mode 100644
index 0000000..6d7fd28
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/FoldInitTypeCheck.cpp
@@ -0,0 +1,140 @@
+//===--- FoldInitTypeCheck.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 "FoldInitTypeCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+void FoldInitTypeCheck::registerMatchers(MatchFinder *Finder) {
+ // We match functions of interest and bind the iterator and init value types.
+ // Note: Right now we check only builtin types.
+ const auto BuiltinTypeWithId = [](const char *ID) {
+ return hasCanonicalType(builtinType().bind(ID));
+ };
+ const auto IteratorWithValueType = [&BuiltinTypeWithId](const char *ID) {
+ return anyOf(
+ // Pointer types.
+ pointsTo(BuiltinTypeWithId(ID)),
+ // Iterator types.
+ recordType(hasDeclaration(has(typedefNameDecl(
+ hasName("value_type"), hasType(BuiltinTypeWithId(ID)))))));
+ };
+
+ const auto IteratorParam = parmVarDecl(
+ hasType(hasCanonicalType(IteratorWithValueType("IterValueType"))));
+ const auto Iterator2Param = parmVarDecl(
+ hasType(hasCanonicalType(IteratorWithValueType("Iter2ValueType"))));
+ const auto InitParam = parmVarDecl(hasType(BuiltinTypeWithId("InitType")));
+
+ // std::accumulate, std::reduce.
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(
+ hasAnyName("::std::accumulate", "::std::reduce"),
+ hasParameter(0, IteratorParam), hasParameter(2, InitParam))),
+ argumentCountIs(3))
+ .bind("Call"),
+ this);
+ // std::inner_product.
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(hasName("::std::inner_product"),
+ hasParameter(0, IteratorParam),
+ hasParameter(2, Iterator2Param),
+ hasParameter(3, InitParam))),
+ argumentCountIs(4))
+ .bind("Call"),
+ this);
+ // std::reduce with a policy.
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(hasName("::std::reduce"),
+ hasParameter(1, IteratorParam),
+ hasParameter(3, InitParam))),
+ argumentCountIs(4))
+ .bind("Call"),
+ this);
+ // std::inner_product with a policy.
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(hasName("::std::inner_product"),
+ hasParameter(1, IteratorParam),
+ hasParameter(3, Iterator2Param),
+ hasParameter(4, InitParam))),
+ argumentCountIs(5))
+ .bind("Call"),
+ this);
+}
+
+/// Returns true if ValueType is allowed to fold into InitType, i.e. if:
+/// static_cast<InitType>(ValueType{some_value})
+/// does not result in trucation.
+static bool isValidBuiltinFold(const BuiltinType &ValueType,
+ const BuiltinType &InitType,
+ const ASTContext &Context) {
+ const auto ValueTypeSize = Context.getTypeSize(&ValueType);
+ const auto InitTypeSize = Context.getTypeSize(&InitType);
+ // It's OK to fold a float into a float of bigger or equal size, but not OK to
+ // fold into an int.
+ if (ValueType.isFloatingPoint())
+ return InitType.isFloatingPoint() && InitTypeSize >= ValueTypeSize;
+ // It's OK to fold an int into:
+ // - an int of the same size and signedness.
+ // - a bigger int, regardless of signedness.
+ // - FIXME: should it be a warning to fold into floating point?
+ if (ValueType.isInteger()) {
+ if (InitType.isInteger()) {
+ if (InitType.isSignedInteger() == ValueType.isSignedInteger())
+ return InitTypeSize >= ValueTypeSize;
+ return InitTypeSize > ValueTypeSize;
+ }
+ if (InitType.isFloatingPoint())
+ return InitTypeSize >= ValueTypeSize;
+ }
+ return false;
+}
+
+/// Prints a diagnostic if IterValueType doe snot fold into IterValueType (see
+// isValidBuiltinFold for details).
+void FoldInitTypeCheck::doCheck(const BuiltinType &IterValueType,
+ const BuiltinType &InitType,
+ const ASTContext &Context,
+ const CallExpr &CallNode) {
+ if (!isValidBuiltinFold(IterValueType, InitType, Context)) {
+ diag(CallNode.getExprLoc(), "folding type %0 into type %1 might result in "
+ "loss of precision")
+ << IterValueType.desugar() << InitType.desugar();
+ }
+}
+
+void FoldInitTypeCheck::check(const MatchFinder::MatchResult &Result) {
+ // Given the iterator and init value type retreived by the matchers,
+ // we check that the ::value_type of the iterator is compatible with
+ // the init value type.
+ const auto *InitType = Result.Nodes.getNodeAs<BuiltinType>("InitType");
+ const auto *IterValueType =
+ Result.Nodes.getNodeAs<BuiltinType>("IterValueType");
+ assert(InitType != nullptr);
+ assert(IterValueType != nullptr);
+
+ const auto *CallNode = Result.Nodes.getNodeAs<CallExpr>("Call");
+ assert(CallNode != nullptr);
+
+ doCheck(*IterValueType, *InitType, *Result.Context, *CallNode);
+
+ if (const auto *Iter2ValueType =
+ Result.Nodes.getNodeAs<BuiltinType>("Iter2ValueType"))
+ doCheck(*Iter2ValueType, *InitType, *Result.Context, *CallNode);
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tools-extra/clang-tidy/bugprone/FoldInitTypeCheck.h b/clang-tools-extra/clang-tidy/bugprone/FoldInitTypeCheck.h
new file mode 100644
index 0000000..e6170de0
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/FoldInitTypeCheck.h
@@ -0,0 +1,44 @@
+//===--- FoldInitTypeCheck.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_BUGPRONE_FOLD_INIT_TYPE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_FOLD_INIT_TYPE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// Find and flag invalid initializer values in folds, e.g. std::accumulate.
+/// Example:
+/// \code
+/// auto v = {65536L * 65536 * 65536};
+/// std::accumulate(begin(v), end(v), 0 /* int type is too small */);
+/// \endcode
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-fold-init-type.html
+class FoldInitTypeCheck : public ClangTidyCheck {
+public:
+ FoldInitTypeCheck(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 doCheck(const BuiltinType &IterValueType, const BuiltinType &InitType,
+ const ASTContext &Context, const CallExpr &CallNode);
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_FOLD_INIT_TYPE_H
diff --git a/clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.cpp
new file mode 100644
index 0000000..9ea5b55
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.cpp
@@ -0,0 +1,174 @@
+//===--- ForwardDeclarationNamespaceCheck.cpp - clang-tidy ------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ForwardDeclarationNamespaceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include <stack>
+#include <string>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+void ForwardDeclarationNamespaceCheck::registerMatchers(MatchFinder *Finder) {
+ // Match all class declarations/definitions *EXCEPT*
+ // 1. implicit classes, e.g. `class A {};` has implicit `class A` inside `A`.
+ // 2. nested classes declared/defined inside another class.
+ // 3. template class declaration, template instantiation or
+ // specialization (NOTE: extern specialization is filtered out by
+ // `unless(hasAncestor(cxxRecordDecl()))`).
+ auto IsInSpecialization = hasAncestor(
+ decl(anyOf(cxxRecordDecl(isExplicitTemplateSpecialization()),
+ functionDecl(isExplicitTemplateSpecialization()))));
+ Finder->addMatcher(
+ cxxRecordDecl(
+ hasParent(decl(anyOf(namespaceDecl(), translationUnitDecl()))),
+ unless(isImplicit()), unless(hasAncestor(cxxRecordDecl())),
+ unless(isInstantiated()), unless(IsInSpecialization),
+ unless(classTemplateSpecializationDecl()))
+ .bind("record_decl"),
+ this);
+
+ // Match all friend declarations. Classes used in friend declarations are not
+ // marked as referenced in AST. We need to record all record classes used in
+ // friend declarations.
+ Finder->addMatcher(friendDecl().bind("friend_decl"), this);
+}
+
+void ForwardDeclarationNamespaceCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ if (const auto *RecordDecl =
+ Result.Nodes.getNodeAs<CXXRecordDecl>("record_decl")) {
+ StringRef DeclName = RecordDecl->getName();
+ if (RecordDecl->isThisDeclarationADefinition()) {
+ DeclNameToDefinitions[DeclName].push_back(RecordDecl);
+ } else {
+ // If a declaration has no definition, the definition could be in another
+ // namespace (a wrong namespace).
+ // NOTE: even a declaration does have definition, we still need it to
+ // compare with other declarations.
+ DeclNameToDeclarations[DeclName].push_back(RecordDecl);
+ }
+ } else {
+ const auto *Decl = Result.Nodes.getNodeAs<FriendDecl>("friend_decl");
+ assert(Decl && "Decl is neither record_decl nor friend decl!");
+
+ // Classes used in friend delarations are not marked referenced in AST,
+ // so we need to check classes used in friend declarations manually to
+ // reduce the rate of false positive.
+ // For example, in
+ // \code
+ // struct A;
+ // struct B { friend A; };
+ // \endcode
+ // `A` will not be marked as "referenced" in the AST.
+ if (const TypeSourceInfo *Tsi = Decl->getFriendType()) {
+ QualType Desugared = Tsi->getType().getDesugaredType(*Result.Context);
+ FriendTypes.insert(Desugared.getTypePtr());
+ }
+ }
+}
+
+static bool haveSameNamespaceOrTranslationUnit(const CXXRecordDecl *Decl1,
+ const CXXRecordDecl *Decl2) {
+ const DeclContext *ParentDecl1 = Decl1->getLexicalParent();
+ const DeclContext *ParentDecl2 = Decl2->getLexicalParent();
+
+ // Since we only matched declarations whose parent is Namespace or
+ // TranslationUnit declaration, the parent should be either a translation unit
+ // or namespace.
+ if (ParentDecl1->getDeclKind() == Decl::TranslationUnit ||
+ ParentDecl2->getDeclKind() == Decl::TranslationUnit) {
+ return ParentDecl1 == ParentDecl2;
+ }
+ assert(ParentDecl1->getDeclKind() == Decl::Namespace &&
+ "ParentDecl1 declaration must be a namespace");
+ assert(ParentDecl2->getDeclKind() == Decl::Namespace &&
+ "ParentDecl2 declaration must be a namespace");
+ auto *Ns1 = NamespaceDecl::castFromDeclContext(ParentDecl1);
+ auto *Ns2 = NamespaceDecl::castFromDeclContext(ParentDecl2);
+ return Ns1->getOriginalNamespace() == Ns2->getOriginalNamespace();
+}
+
+static std::string getNameOfNamespace(const CXXRecordDecl *Decl) {
+ const auto *ParentDecl = Decl->getLexicalParent();
+ if (ParentDecl->getDeclKind() == Decl::TranslationUnit) {
+ return "(global)";
+ }
+ const auto *NsDecl = cast<NamespaceDecl>(ParentDecl);
+ std::string Ns;
+ llvm::raw_string_ostream OStream(Ns);
+ NsDecl->printQualifiedName(OStream);
+ OStream.flush();
+ return Ns.empty() ? "(global)" : Ns;
+}
+
+void ForwardDeclarationNamespaceCheck::onEndOfTranslationUnit() {
+ // Iterate each group of declarations by name.
+ for (const auto &KeyValuePair : DeclNameToDeclarations) {
+ const auto &Declarations = KeyValuePair.second;
+ // If more than 1 declaration exists, we check if all are in the same
+ // namespace.
+ for (const auto *CurDecl : Declarations) {
+ if (CurDecl->hasDefinition() || CurDecl->isReferenced()) {
+ continue; // Skip forward declarations that are used/referenced.
+ }
+ if (FriendTypes.count(CurDecl->getTypeForDecl()) != 0) {
+ continue; // Skip forward declarations referenced as friend.
+ }
+ if (CurDecl->getLocation().isMacroID() ||
+ CurDecl->getLocation().isInvalid()) {
+ continue;
+ }
+ // Compare with all other declarations with the same name.
+ for (const auto *Decl : Declarations) {
+ if (Decl == CurDecl) {
+ continue; // Don't compare with self.
+ }
+ if (!CurDecl->hasDefinition() &&
+ !haveSameNamespaceOrTranslationUnit(CurDecl, Decl)) {
+ diag(CurDecl->getLocation(),
+ "declaration %0 is never referenced, but a declaration with "
+ "the same name found in another namespace '%1'")
+ << CurDecl << getNameOfNamespace(Decl);
+ diag(Decl->getLocation(), "a declaration of %0 is found here",
+ DiagnosticIDs::Note)
+ << Decl;
+ break; // FIXME: We only generate one warning for each declaration.
+ }
+ }
+ // Check if a definition in another namespace exists.
+ const auto DeclName = CurDecl->getName();
+ if (DeclNameToDefinitions.find(DeclName) == DeclNameToDefinitions.end()) {
+ continue; // No definition in this translation unit, we can skip it.
+ }
+ // Make a warning for each definition with the same name (in other
+ // namespaces).
+ const auto &Definitions = DeclNameToDefinitions[DeclName];
+ for (const auto *Def : Definitions) {
+ diag(CurDecl->getLocation(),
+ "no definition found for %0, but a definition with "
+ "the same name %1 found in another namespace '%2'")
+ << CurDecl << Def << getNameOfNamespace(Def);
+ diag(Def->getLocation(), "a definition of %0 is found here",
+ DiagnosticIDs::Note)
+ << Def;
+ }
+ }
+ }
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.h b/clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.h
new file mode 100644
index 0000000..c3d3018
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.h
@@ -0,0 +1,59 @@
+//===--- ForwardDeclarationNamespaceCheck.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_BUGPRONE_FORWARDDECLARATIONNAMESPACECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_FORWARDDECLARATIONNAMESPACECHECK_H
+
+#include "../ClangTidy.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include <set>
+#include <vector>
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// Checks if an unused forward declaration is in a wrong namespace.
+///
+/// The check inspects all unused forward declarations and checks if there is
+/// any declaration/definition with the same name, which could indicate
+/// that the forward declaration is potentially in a wrong namespace.
+///
+/// \code
+/// namespace na { struct A; }
+/// namespace nb { struct A {} };
+/// nb::A a;
+/// // warning : no definition found for 'A', but a definition with the same
+/// name 'A' found in another namespace 'nb::'
+/// \endcode
+///
+/// This check can only generate warnings, but it can't suggest fixes at this
+/// point.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-forward-declaration-namespace.html
+class ForwardDeclarationNamespaceCheck : public ClangTidyCheck {
+public:
+ ForwardDeclarationNamespaceCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ void onEndOfTranslationUnit() override;
+
+private:
+ llvm::StringMap<std::vector<const CXXRecordDecl *>> DeclNameToDefinitions;
+ llvm::StringMap<std::vector<const CXXRecordDecl *>> DeclNameToDeclarations;
+ llvm::SmallPtrSet<const Type *, 16> FriendTypes;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_FORWARDDECLARATIONNAMESPACECHECK_H
diff --git a/clang-tools-extra/clang-tidy/bugprone/InaccurateEraseCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/InaccurateEraseCheck.cpp
new file mode 100644
index 0000000..cf1be0e
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/InaccurateEraseCheck.cpp
@@ -0,0 +1,81 @@
+//===--- InaccurateEraseCheck.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 "InaccurateEraseCheck.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 bugprone {
+
+namespace {
+AST_MATCHER(Decl, isInStdNamespace) { return Node.isInStdNamespace(); }
+}
+
+void InaccurateEraseCheck::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 EndCall =
+ callExpr(
+ callee(functionDecl(hasAnyName("remove", "remove_if", "unique"))),
+ hasArgument(
+ 1,
+ anyOf(cxxConstructExpr(has(ignoringImplicit(
+ cxxMemberCallExpr(callee(cxxMethodDecl(hasName("end"))))
+ .bind("end")))),
+ anything())))
+ .bind("alg");
+
+ const auto DeclInStd = type(hasUnqualifiedDesugaredType(
+ tagType(hasDeclaration(decl(isInStdNamespace())))));
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+ on(anyOf(hasType(DeclInStd), hasType(pointsTo(DeclInStd)))),
+ callee(cxxMethodDecl(hasName("erase"))), argumentCountIs(1),
+ hasArgument(0, has(ignoringImplicit(
+ anyOf(EndCall, has(ignoringImplicit(EndCall)))))),
+ unless(isInTemplateInstantiation()))
+ .bind("erase"),
+ this);
+}
+
+void InaccurateEraseCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MemberCall =
+ Result.Nodes.getNodeAs<CXXMemberCallExpr>("erase");
+ const auto *EndExpr =
+ Result.Nodes.getNodeAs<CXXMemberCallExpr>("end");
+ const SourceLocation Loc = MemberCall->getLocStart();
+
+ FixItHint Hint;
+
+ if (!Loc.isMacroID() && EndExpr) {
+ const auto *AlgCall = Result.Nodes.getNodeAs<CallExpr>("alg");
+ std::string ReplacementText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(EndExpr->getSourceRange()),
+ *Result.SourceManager, getLangOpts());
+ const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+ AlgCall->getLocEnd(), 0, *Result.SourceManager, getLangOpts());
+ Hint = FixItHint::CreateInsertion(EndLoc, ", " + ReplacementText);
+ }
+
+ diag(Loc, "this call will remove at most one item even when multiple items "
+ "should be removed")
+ << Hint;
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tools-extra/clang-tidy/bugprone/InaccurateEraseCheck.h b/clang-tools-extra/clang-tidy/bugprone/InaccurateEraseCheck.h
new file mode 100644
index 0000000..d6b3729
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/InaccurateEraseCheck.h
@@ -0,0 +1,38 @@
+//===--- InaccurateEraseCheck.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_BUGPRONE_INACCURATEERASECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_INACCURATEERASECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// Checks for inaccurate use of the `erase()` method.
+///
+/// Algorithms like `remove()` do not actually remove any element from the
+/// container but return an iterator to the first redundant element at the end
+/// of the container. These redundant elements must be removed using the
+/// `erase()` method. This check warns when not all of the elements will be
+/// removed due to using an inappropriate overload.
+class InaccurateEraseCheck : public ClangTidyCheck {
+public:
+ InaccurateEraseCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_INACCURATEERASECHECK_H
diff --git a/clang-tools-extra/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp
new file mode 100644
index 0000000..516ee19
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp
@@ -0,0 +1,133 @@
+//===--- MoveForwardingReferenceCheck.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 "MoveForwardingReferenceCheck.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <algorithm>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+static void replaceMoveWithForward(const UnresolvedLookupExpr *Callee,
+ const ParmVarDecl *ParmVar,
+ const TemplateTypeParmDecl *TypeParmDecl,
+ DiagnosticBuilder &Diag,
+ const ASTContext &Context) {
+ const SourceManager &SM = Context.getSourceManager();
+ const LangOptions &LangOpts = Context.getLangOpts();
+
+ CharSourceRange CallRange =
+ Lexer::makeFileCharRange(CharSourceRange::getTokenRange(
+ Callee->getLocStart(), Callee->getLocEnd()),
+ SM, LangOpts);
+
+ if (CallRange.isValid()) {
+ const std::string TypeName =
+ TypeParmDecl->getIdentifier()
+ ? TypeParmDecl->getName().str()
+ : (llvm::Twine("decltype(") + ParmVar->getName() + ")").str();
+
+ const std::string ForwardName =
+ (llvm::Twine("forward<") + TypeName + ">").str();
+
+ // Create a replacement only if we see a "standard" way of calling
+ // std::move(). This will hopefully prevent erroneous replacements if the
+ // code does unusual things (e.g. create an alias for std::move() in
+ // another namespace).
+ NestedNameSpecifier *NNS = Callee->getQualifier();
+ if (!NNS) {
+ // Called as "move" (i.e. presumably the code had a "using std::move;").
+ // We still conservatively put a "std::" in front of the forward because
+ // we don't know whether the code also had a "using std::forward;".
+ Diag << FixItHint::CreateReplacement(CallRange, "std::" + ForwardName);
+ } else if (const NamespaceDecl *Namespace = NNS->getAsNamespace()) {
+ if (Namespace->getName() == "std") {
+ if (!NNS->getPrefix()) {
+ // Called as "std::move".
+ Diag << FixItHint::CreateReplacement(CallRange,
+ "std::" + ForwardName);
+ } else if (NNS->getPrefix()->getKind() == NestedNameSpecifier::Global) {
+ // Called as "::std::move".
+ Diag << FixItHint::CreateReplacement(CallRange,
+ "::std::" + ForwardName);
+ }
+ }
+ }
+ }
+}
+
+void MoveForwardingReferenceCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus11)
+ return;
+
+ // Matches a ParmVarDecl for a forwarding reference, i.e. a non-const rvalue
+ // reference of a function template parameter type.
+ auto ForwardingReferenceParmMatcher =
+ parmVarDecl(
+ hasType(qualType(rValueReferenceType(),
+ references(templateTypeParmType(hasDeclaration(
+ templateTypeParmDecl().bind("type-parm-decl")))),
+ unless(references(qualType(isConstQualified()))))))
+ .bind("parm-var");
+
+ Finder->addMatcher(
+ callExpr(callee(unresolvedLookupExpr(
+ hasAnyDeclaration(namedDecl(
+ hasUnderlyingDecl(hasName("::std::move")))))
+ .bind("lookup")),
+ argumentCountIs(1),
+ hasArgument(0, ignoringParenImpCasts(declRefExpr(
+ to(ForwardingReferenceParmMatcher)))))
+ .bind("call-move"),
+ this);
+}
+
+void MoveForwardingReferenceCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move");
+ const auto *UnresolvedLookup =
+ Result.Nodes.getNodeAs<UnresolvedLookupExpr>("lookup");
+ const auto *ParmVar = Result.Nodes.getNodeAs<ParmVarDecl>("parm-var");
+ const auto *TypeParmDecl =
+ Result.Nodes.getNodeAs<TemplateTypeParmDecl>("type-parm-decl");
+
+ // Get the FunctionDecl and FunctionTemplateDecl containing the function
+ // parameter.
+ const auto *FuncForParam = dyn_cast<FunctionDecl>(ParmVar->getDeclContext());
+ if (!FuncForParam)
+ return;
+ const FunctionTemplateDecl *FuncTemplate =
+ FuncForParam->getDescribedFunctionTemplate();
+ if (!FuncTemplate)
+ return;
+
+ // Check that the template type parameter belongs to the same function
+ // template as the function parameter of that type. (This implies that type
+ // deduction will happen on the type.)
+ const TemplateParameterList *Params = FuncTemplate->getTemplateParameters();
+ if (!std::count(Params->begin(), Params->end(), TypeParmDecl))
+ return;
+
+ auto Diag = diag(CallMove->getExprLoc(),
+ "forwarding reference passed to std::move(), which may "
+ "unexpectedly cause lvalues to be moved; use "
+ "std::forward() instead");
+
+ replaceMoveWithForward(UnresolvedLookup, ParmVar, TypeParmDecl, Diag,
+ *Result.Context);
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tools-extra/clang-tidy/bugprone/MoveForwardingReferenceCheck.h b/clang-tools-extra/clang-tidy/bugprone/MoveForwardingReferenceCheck.h
new file mode 100644
index 0000000..c61de75
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/MoveForwardingReferenceCheck.h
@@ -0,0 +1,49 @@
+//===--- MoveForwardingReferenceCheck.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_BUGPRONE_MOVEFORWARDINGREFERENCECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MOVEFORWARDINGREFERENCECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// The check warns if std::move is applied to a forwarding reference (i.e. an
+/// rvalue reference of a function template argument type).
+///
+/// If a developer is unaware of the special rules for template argument
+/// deduction on forwarding references, it will seem reasonable to apply
+/// std::move to the forwarding reference, in the same way that this would be
+/// done for a "normal" rvalue reference.
+///
+/// This has a consequence that is usually unwanted and possibly surprising: if
+/// the function that takes the forwarding reference as its parameter is called
+/// with an lvalue, that lvalue will be moved from (and hence placed into an
+/// indeterminate state) even though no std::move was applied to the lvalue at
+/// the call site.
+//
+/// The check suggests replacing the std::move with a std::forward.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-move-forwarding-reference.html
+class MoveForwardingReferenceCheck : public ClangTidyCheck {
+public:
+ MoveForwardingReferenceCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MOVEFORWARDINGREFERENCECHECK_H
diff --git a/clang-tools-extra/clang-tidy/bugprone/MultipleStatementMacroCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/MultipleStatementMacroCheck.cpp
new file mode 100644
index 0000000..9fa2f43
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/MultipleStatementMacroCheck.cpp
@@ -0,0 +1,106 @@
+//===--- MultipleStatementMacroCheck.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 "MultipleStatementMacroCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+namespace {
+
+AST_MATCHER(Expr, isInMacro) { return Node.getLocStart().isMacroID(); }
+
+/// \brief Find the next statement after `S`.
+const Stmt *nextStmt(const MatchFinder::MatchResult &Result, const Stmt *S) {
+ auto Parents = Result.Context->getParents(*S);
+ if (Parents.empty())
+ return nullptr;
+ const auto *Parent = Parents[0].get<Stmt>();
+ if (!Parent)
+ return nullptr;
+ const Stmt *Prev = nullptr;
+ for (const Stmt *Child : Parent->children()) {
+ if (Prev == S)
+ return Child;
+ Prev = Child;
+ }
+ return nextStmt(Result, Parent);
+}
+
+using ExpansionRanges = std::vector<std::pair<SourceLocation, SourceLocation>>;
+
+/// \bried Get all the macro expansion ranges related to `Loc`.
+///
+/// The result is ordered from most inner to most outer.
+ExpansionRanges getExpansionRanges(SourceLocation Loc,
+ const MatchFinder::MatchResult &Result) {
+ ExpansionRanges Locs;
+ while (Loc.isMacroID()) {
+ Locs.push_back(Result.SourceManager->getImmediateExpansionRange(Loc));
+ Loc = Locs.back().first;
+ }
+ return Locs;
+}
+
+} // namespace
+
+void MultipleStatementMacroCheck::registerMatchers(MatchFinder *Finder) {
+ const auto Inner = expr(isInMacro(), unless(compoundStmt())).bind("inner");
+ Finder->addMatcher(
+ stmt(anyOf(ifStmt(hasThen(Inner)), ifStmt(hasElse(Inner)).bind("else"),
+ whileStmt(hasBody(Inner)), forStmt(hasBody(Inner))))
+ .bind("outer"),
+ this);
+}
+
+void MultipleStatementMacroCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *Inner = Result.Nodes.getNodeAs<Expr>("inner");
+ const auto *Outer = Result.Nodes.getNodeAs<Stmt>("outer");
+ const auto *Next = nextStmt(Result, Outer);
+ if (!Next)
+ return;
+
+ SourceLocation OuterLoc = Outer->getLocStart();
+ if (Result.Nodes.getNodeAs<Stmt>("else"))
+ OuterLoc = cast<IfStmt>(Outer)->getElseLoc();
+
+ auto InnerRanges = getExpansionRanges(Inner->getLocStart(), Result);
+ auto OuterRanges = getExpansionRanges(OuterLoc, Result);
+ auto NextRanges = getExpansionRanges(Next->getLocStart(), Result);
+
+ // Remove all the common ranges, starting from the top (the last ones in the
+ // list).
+ while (!InnerRanges.empty() && !OuterRanges.empty() && !NextRanges.empty() &&
+ InnerRanges.back() == OuterRanges.back() &&
+ InnerRanges.back() == NextRanges.back()) {
+ InnerRanges.pop_back();
+ OuterRanges.pop_back();
+ NextRanges.pop_back();
+ }
+
+ // Inner and Next must have at least one more macro that Outer doesn't have,
+ // and that range must be common to both.
+ if (InnerRanges.empty() || NextRanges.empty() ||
+ InnerRanges.back() != NextRanges.back())
+ return;
+
+ diag(InnerRanges.back().first, "multiple statement macro used without "
+ "braces; some statements will be "
+ "unconditionally executed");
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tools-extra/clang-tidy/bugprone/MultipleStatementMacroCheck.h b/clang-tools-extra/clang-tidy/bugprone/MultipleStatementMacroCheck.h
new file mode 100644
index 0000000..efc6599
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/MultipleStatementMacroCheck.h
@@ -0,0 +1,37 @@
+//===--- MultipleStatementMacroCheck.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_BUGPRONE_MULTIPLE_STATEMENT_MACRO_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MULTIPLE_STATEMENT_MACRO_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// Detect multiple statement macros that are used in unbraced conditionals.
+/// Only the first statement of the macro will be inside the conditional and the
+/// other ones will be executed unconditionally.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-multiple-statement-macro.html
+class MultipleStatementMacroCheck : public ClangTidyCheck {
+public:
+ MultipleStatementMacroCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MULTIPLE_STATEMENT_MACRO_H
diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
new file mode 100644
index 0000000..6fa4cab
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
@@ -0,0 +1,434 @@
+//===--- UseAfterMoveCheck.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 "UseAfterMoveCheck.h"
+
+#include "clang/Analysis/CFG.h"
+#include "clang/Lex/Lexer.h"
+
+#include "../utils/ExprSequence.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::utils;
+
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+namespace {
+
+/// Contains information about a use-after-move.
+struct UseAfterMove {
+ // The DeclRefExpr that constituted the use of the object.
+ const DeclRefExpr *DeclRef;
+
+ // Is the order in which the move and the use are evaluated undefined?
+ bool EvaluationOrderUndefined;
+};
+
+/// Finds uses of a variable after a move (and maintains state required by the
+/// various internal helper functions).
+class UseAfterMoveFinder {
+public:
+ UseAfterMoveFinder(ASTContext *TheContext);
+
+ // Within the given function body, finds the first use of 'MovedVariable' that
+ // occurs after 'MovingCall' (the expression that performs the move). If a
+ // use-after-move is found, writes information about it to 'TheUseAfterMove'.
+ // Returns whether a use-after-move was found.
+ bool find(Stmt *FunctionBody, const Expr *MovingCall,
+ const ValueDecl *MovedVariable, UseAfterMove *TheUseAfterMove);
+
+private:
+ bool findInternal(const CFGBlock *Block, const Expr *MovingCall,
+ const ValueDecl *MovedVariable,
+ UseAfterMove *TheUseAfterMove);
+ void getUsesAndReinits(const CFGBlock *Block, const ValueDecl *MovedVariable,
+ llvm::SmallVectorImpl<const DeclRefExpr *> *Uses,
+ llvm::SmallPtrSetImpl<const Stmt *> *Reinits);
+ void getDeclRefs(const CFGBlock *Block, const Decl *MovedVariable,
+ llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs);
+ void getReinits(const CFGBlock *Block, const ValueDecl *MovedVariable,
+ llvm::SmallPtrSetImpl<const Stmt *> *Stmts,
+ llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs);
+
+ ASTContext *Context;
+ std::unique_ptr<ExprSequence> Sequence;
+ std::unique_ptr<StmtToBlockMap> BlockMap;
+ llvm::SmallPtrSet<const CFGBlock *, 8> Visited;
+};
+
+} // namespace
+
+
+// Matches nodes that are
+// - Part of a decltype argument or class template argument (we check this by
+// seeing if they are children of a TypeLoc), or
+// - Part of a function template argument (we check this by seeing if they are
+// children of a DeclRefExpr that references a function template).
+// DeclRefExprs that fulfill these conditions should not be counted as a use or
+// move.
+static StatementMatcher inDecltypeOrTemplateArg() {
+ return anyOf(hasAncestor(typeLoc()),
+ hasAncestor(declRefExpr(
+ to(functionDecl(ast_matchers::isTemplateInstantiation())))));
+}
+
+UseAfterMoveFinder::UseAfterMoveFinder(ASTContext *TheContext)
+ : Context(TheContext) {}
+
+bool UseAfterMoveFinder::find(Stmt *FunctionBody, const Expr *MovingCall,
+ const ValueDecl *MovedVariable,
+ UseAfterMove *TheUseAfterMove) {
+ // Generate the CFG manually instead of through an AnalysisDeclContext because
+ // it seems the latter can't be used to generate a CFG for the body of a
+ // labmda.
+ //
+ // We include implicit and temporary destructors in the CFG so that
+ // destructors marked [[noreturn]] are handled correctly in the control flow
+ // analysis. (These are used in some styles of assertion macros.)
+ CFG::BuildOptions Options;
+ Options.AddImplicitDtors = true;
+ Options.AddTemporaryDtors = true;
+ std::unique_ptr<CFG> TheCFG =
+ CFG::buildCFG(nullptr, FunctionBody, Context, Options);
+ if (!TheCFG)
+ return false;
+
+ Sequence.reset(new ExprSequence(TheCFG.get(), Context));
+ BlockMap.reset(new StmtToBlockMap(TheCFG.get(), Context));
+ Visited.clear();
+
+ const CFGBlock *Block = BlockMap->blockContainingStmt(MovingCall);
+ if (!Block)
+ return false;
+
+ return findInternal(Block, MovingCall, MovedVariable, TheUseAfterMove);
+}
+
+bool UseAfterMoveFinder::findInternal(const CFGBlock *Block,
+ const Expr *MovingCall,
+ const ValueDecl *MovedVariable,
+ UseAfterMove *TheUseAfterMove) {
+ if (Visited.count(Block))
+ return false;
+
+ // Mark the block as visited (except if this is the block containing the
+ // std::move() and it's being visited the first time).
+ if (!MovingCall)
+ Visited.insert(Block);
+
+ // Get all uses and reinits in the block.
+ llvm::SmallVector<const DeclRefExpr *, 1> Uses;
+ llvm::SmallPtrSet<const Stmt *, 1> Reinits;
+ getUsesAndReinits(Block, MovedVariable, &Uses, &Reinits);
+
+ // Ignore all reinitializations where the move potentially comes after the
+ // reinit.
+ llvm::SmallVector<const Stmt *, 1> ReinitsToDelete;
+ for (const Stmt *Reinit : Reinits) {
+ if (MovingCall && Sequence->potentiallyAfter(MovingCall, Reinit))
+ ReinitsToDelete.push_back(Reinit);
+ }
+ for (const Stmt *Reinit : ReinitsToDelete) {
+ Reinits.erase(Reinit);
+ }
+
+ // Find all uses that potentially come after the move.
+ for (const DeclRefExpr *Use : Uses) {
+ if (!MovingCall || Sequence->potentiallyAfter(Use, MovingCall)) {
+ // Does the use have a saving reinit? A reinit is saving if it definitely
+ // comes before the use, i.e. if there's no potential that the reinit is
+ // after the use.
+ bool HaveSavingReinit = false;
+ for (const Stmt *Reinit : Reinits) {
+ if (!Sequence->potentiallyAfter(Reinit, Use))
+ HaveSavingReinit = true;
+ }
+
+ if (!HaveSavingReinit) {
+ TheUseAfterMove->DeclRef = Use;
+
+ // Is this a use-after-move that depends on order of evaluation?
+ // This is the case if the move potentially comes after the use (and we
+ // already know that use potentially comes after the move, which taken
+ // together tells us that the ordering is unclear).
+ TheUseAfterMove->EvaluationOrderUndefined =
+ MovingCall != nullptr &&
+ Sequence->potentiallyAfter(MovingCall, Use);
+
+ return true;
+ }
+ }
+ }
+
+ // If the object wasn't reinitialized, call ourselves recursively on all
+ // successors.
+ if (Reinits.empty()) {
+ for (const auto &Succ : Block->succs()) {
+ if (Succ && findInternal(Succ, nullptr, MovedVariable, TheUseAfterMove))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void UseAfterMoveFinder::getUsesAndReinits(
+ const CFGBlock *Block, const ValueDecl *MovedVariable,
+ llvm::SmallVectorImpl<const DeclRefExpr *> *Uses,
+ llvm::SmallPtrSetImpl<const Stmt *> *Reinits) {
+ llvm::SmallPtrSet<const DeclRefExpr *, 1> DeclRefs;
+ llvm::SmallPtrSet<const DeclRefExpr *, 1> ReinitDeclRefs;
+
+ getDeclRefs(Block, MovedVariable, &DeclRefs);
+ getReinits(Block, MovedVariable, Reinits, &ReinitDeclRefs);
+
+ // All references to the variable that aren't reinitializations are uses.
+ Uses->clear();
+ for (const DeclRefExpr *DeclRef : DeclRefs) {
+ if (!ReinitDeclRefs.count(DeclRef))
+ Uses->push_back(DeclRef);
+ }
+
+ // Sort the uses by their occurrence in the source code.
+ std::sort(Uses->begin(), Uses->end(),
+ [](const DeclRefExpr *D1, const DeclRefExpr *D2) {
+ return D1->getExprLoc() < D2->getExprLoc();
+ });
+}
+
+bool isStandardSmartPointer(const ValueDecl *VD) {
+ const Type *TheType = VD->getType().getTypePtrOrNull();
+ if (!TheType)
+ return false;
+
+ const CXXRecordDecl *RecordDecl = TheType->getAsCXXRecordDecl();
+ if (!RecordDecl)
+ return false;
+
+ const IdentifierInfo *ID = RecordDecl->getIdentifier();
+ if (!ID)
+ return false;
+
+ StringRef Name = ID->getName();
+ if (Name != "unique_ptr" && Name != "shared_ptr" && Name != "weak_ptr")
+ return false;
+
+ return RecordDecl->getDeclContext()->isStdNamespace();
+}
+
+void UseAfterMoveFinder::getDeclRefs(
+ const CFGBlock *Block, const Decl *MovedVariable,
+ llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs) {
+ DeclRefs->clear();
+ for (const auto &Elem : *Block) {
+ Optional<CFGStmt> S = Elem.getAs<CFGStmt>();
+ if (!S)
+ continue;
+
+ auto addDeclRefs = [this, Block,
+ DeclRefs](const ArrayRef<BoundNodes> Matches) {
+ for (const auto &Match : Matches) {
+ const auto *DeclRef = Match.getNodeAs<DeclRefExpr>("declref");
+ const auto *Operator = Match.getNodeAs<CXXOperatorCallExpr>("operator");
+ if (DeclRef && BlockMap->blockContainingStmt(DeclRef) == Block) {
+ // Ignore uses of a standard smart pointer that don't dereference the
+ // pointer.
+ if (Operator || !isStandardSmartPointer(DeclRef->getDecl())) {
+ DeclRefs->insert(DeclRef);
+ }
+ }
+ }
+ };
+
+ auto DeclRefMatcher = declRefExpr(hasDeclaration(equalsNode(MovedVariable)),
+ unless(inDecltypeOrTemplateArg()))
+ .bind("declref");
+
+ addDeclRefs(match(findAll(DeclRefMatcher), *S->getStmt(), *Context));
+ addDeclRefs(match(
+ findAll(cxxOperatorCallExpr(anyOf(hasOverloadedOperatorName("*"),
+ hasOverloadedOperatorName("->"),
+ hasOverloadedOperatorName("[]")),
+ hasArgument(0, DeclRefMatcher))
+ .bind("operator")),
+ *S->getStmt(), *Context));
+ }
+}
+
+void UseAfterMoveFinder::getReinits(
+ const CFGBlock *Block, const ValueDecl *MovedVariable,
+ llvm::SmallPtrSetImpl<const Stmt *> *Stmts,
+ llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs) {
+ auto DeclRefMatcher =
+ declRefExpr(hasDeclaration(equalsNode(MovedVariable))).bind("declref");
+
+ auto StandardContainerTypeMatcher = hasType(hasUnqualifiedDesugaredType(
+ recordType(hasDeclaration(cxxRecordDecl(hasAnyName(
+ "::std::basic_string", "::std::vector", "::std::deque",
+ "::std::forward_list", "::std::list", "::std::set", "::std::map",
+ "::std::multiset", "::std::multimap", "::std::unordered_set",
+ "::std::unordered_map", "::std::unordered_multiset",
+ "::std::unordered_multimap"))))));
+
+ auto StandardSmartPointerTypeMatcher = hasType(hasUnqualifiedDesugaredType(
+ recordType(hasDeclaration(cxxRecordDecl(hasAnyName(
+ "::std::unique_ptr", "::std::shared_ptr", "::std::weak_ptr"))))));
+
+ // Matches different types of reinitialization.
+ auto ReinitMatcher =
+ stmt(anyOf(
+ // Assignment. In addition to the overloaded assignment operator,
+ // test for built-in assignment as well, since template functions
+ // may be instantiated to use std::move() on built-in types.
+ binaryOperator(hasOperatorName("="), hasLHS(DeclRefMatcher)),
+ cxxOperatorCallExpr(hasOverloadedOperatorName("="),
+ hasArgument(0, DeclRefMatcher)),
+ // Declaration. We treat this as a type of reinitialization too,
+ // so we don't need to treat it separately.
+ declStmt(hasDescendant(equalsNode(MovedVariable))),
+ // clear() and assign() on standard containers.
+ cxxMemberCallExpr(
+ on(allOf(DeclRefMatcher, StandardContainerTypeMatcher)),
+ // To keep the matcher simple, we check for assign() calls
+ // on all standard containers, even though only vector,
+ // deque, forward_list and list have assign(). If assign()
+ // is called on any of the other containers, this will be
+ // flagged by a compile error anyway.
+ callee(cxxMethodDecl(hasAnyName("clear", "assign")))),
+ // reset() on standard smart pointers.
+ cxxMemberCallExpr(
+ on(allOf(DeclRefMatcher, StandardSmartPointerTypeMatcher)),
+ callee(cxxMethodDecl(hasName("reset")))),
+ // Passing variable to a function as a non-const pointer.
+ callExpr(forEachArgumentWithParam(
+ unaryOperator(hasOperatorName("&"),
+ hasUnaryOperand(DeclRefMatcher)),
+ unless(parmVarDecl(hasType(pointsTo(isConstQualified())))))),
+ // Passing variable to a function as a non-const lvalue reference
+ // (unless that function is std::move()).
+ callExpr(forEachArgumentWithParam(
+ DeclRefMatcher,
+ unless(parmVarDecl(hasType(
+ references(qualType(isConstQualified())))))),
+ unless(callee(functionDecl(hasName("::std::move")))))))
+ .bind("reinit");
+
+ Stmts->clear();
+ DeclRefs->clear();
+ for (const auto &Elem : *Block) {
+ Optional<CFGStmt> S = Elem.getAs<CFGStmt>();
+ if (!S)
+ continue;
+
+ SmallVector<BoundNodes, 1> Matches =
+ match(findAll(ReinitMatcher), *S->getStmt(), *Context);
+
+ for (const auto &Match : Matches) {
+ const auto *TheStmt = Match.getNodeAs<Stmt>("reinit");
+ const auto *TheDeclRef = Match.getNodeAs<DeclRefExpr>("declref");
+ if (TheStmt && BlockMap->blockContainingStmt(TheStmt) == Block) {
+ Stmts->insert(TheStmt);
+
+ // We count DeclStmts as reinitializations, but they don't have a
+ // DeclRefExpr associated with them -- so we need to check 'TheDeclRef'
+ // before adding it to the set.
+ if (TheDeclRef)
+ DeclRefs->insert(TheDeclRef);
+ }
+ }
+ }
+}
+
+static void emitDiagnostic(const Expr *MovingCall, const DeclRefExpr *MoveArg,
+ const UseAfterMove &Use, ClangTidyCheck *Check,
+ ASTContext *Context) {
+ SourceLocation UseLoc = Use.DeclRef->getExprLoc();
+ SourceLocation MoveLoc = MovingCall->getExprLoc();
+
+ Check->diag(UseLoc, "'%0' used after it was moved")
+ << MoveArg->getDecl()->getName();
+ Check->diag(MoveLoc, "move occurred here", DiagnosticIDs::Note);
+ if (Use.EvaluationOrderUndefined) {
+ Check->diag(UseLoc,
+ "the use and move are unsequenced, i.e. there is no guarantee "
+ "about the order in which they are evaluated",
+ DiagnosticIDs::Note);
+ } else if (UseLoc < MoveLoc || Use.DeclRef == MoveArg) {
+ Check->diag(UseLoc,
+ "the use happens in a later loop iteration than the move",
+ DiagnosticIDs::Note);
+ }
+}
+
+void UseAfterMoveCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus11)
+ return;
+
+ auto CallMoveMatcher =
+ callExpr(callee(functionDecl(hasName("::std::move"))), argumentCountIs(1),
+ hasArgument(0, declRefExpr().bind("arg")),
+ anyOf(hasAncestor(lambdaExpr().bind("containing-lambda")),
+ hasAncestor(functionDecl().bind("containing-func"))),
+ unless(inDecltypeOrTemplateArg()))
+ .bind("call-move");
+
+ Finder->addMatcher(
+ // To find the Stmt that we assume performs the actual move, we look for
+ // the direct ancestor of the std::move() that isn't one of the node
+ // types ignored by ignoringParenImpCasts().
+ stmt(forEach(expr(ignoringParenImpCasts(CallMoveMatcher))),
+ // Don't allow an InitListExpr to be the moving call. An InitListExpr
+ // has both a syntactic and a semantic form, and the parent-child
+ // relationships are different between the two. This could cause an
+ // InitListExpr to be analyzed as the moving call in addition to the
+ // Expr that we actually want, resulting in two diagnostics with
+ // different code locations for the same move.
+ unless(initListExpr()),
+ unless(expr(ignoringParenImpCasts(equalsBoundNode("call-move")))))
+ .bind("moving-call"),
+ this);
+}
+
+void UseAfterMoveCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *ContainingLambda =
+ Result.Nodes.getNodeAs<LambdaExpr>("containing-lambda");
+ const auto *ContainingFunc =
+ Result.Nodes.getNodeAs<FunctionDecl>("containing-func");
+ const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move");
+ const auto *MovingCall = Result.Nodes.getNodeAs<Expr>("moving-call");
+ const auto *Arg = Result.Nodes.getNodeAs<DeclRefExpr>("arg");
+
+ if (!MovingCall || !MovingCall->getExprLoc().isValid())
+ MovingCall = CallMove;
+
+ Stmt *FunctionBody = nullptr;
+ if (ContainingLambda)
+ FunctionBody = ContainingLambda->getBody();
+ else if (ContainingFunc)
+ FunctionBody = ContainingFunc->getBody();
+ else
+ return;
+
+ // Ignore the std::move if the variable that was passed to it isn't a local
+ // variable.
+ if (!Arg->getDecl()->getDeclContext()->isFunctionOrMethod())
+ return;
+
+ UseAfterMoveFinder finder(Result.Context);
+ UseAfterMove Use;
+ if (finder.find(FunctionBody, MovingCall, Arg->getDecl(), &Use))
+ emitDiagnostic(MovingCall, Arg, Use, this, Result.Context);
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.h b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.h
new file mode 100644
index 0000000..f6dea68
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.h
@@ -0,0 +1,36 @@
+//===--- UseAfterMoveCheck.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_BUGPRONE_USEAFTERMOVECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_USEAFTERMOVECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// The check warns if an object is used after it has been moved, without an
+/// intervening reinitialization.
+///
+/// For details, see the user-facing documentation:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-use-after-move.html
+class UseAfterMoveCheck : public ClangTidyCheck {
+public:
+ UseAfterMoveCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_USEAFTERMOVECHECK_H
diff --git a/clang-tools-extra/clang-tidy/bugprone/VirtualNearMissCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/VirtualNearMissCheck.cpp
new file mode 100644
index 0000000..6b3f498
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/VirtualNearMissCheck.cpp
@@ -0,0 +1,274 @@
+//===--- VirtualNearMissCheck.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 "VirtualNearMissCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+AST_MATCHER(CXXMethodDecl, isStatic) { return Node.isStatic(); }
+
+AST_MATCHER(CXXMethodDecl, isOverloadedOperator) {
+ return Node.isOverloadedOperator();
+}
+
+/// Finds out if the given method overrides some method.
+static bool isOverrideMethod(const CXXMethodDecl *MD) {
+ return MD->size_overridden_methods() > 0 || MD->hasAttr<OverrideAttr>();
+}
+
+/// Checks whether the return types are covariant, according to
+/// C++[class.virtual]p7.
+///
+/// Similar with clang::Sema::CheckOverridingFunctionReturnType.
+/// \returns true if the return types of BaseMD and DerivedMD are covariant.
+static bool checkOverridingFunctionReturnType(const ASTContext *Context,
+ const CXXMethodDecl *BaseMD,
+ const CXXMethodDecl *DerivedMD) {
+ QualType BaseReturnTy = BaseMD->getType()
+ ->getAs<FunctionType>()
+ ->getReturnType()
+ .getCanonicalType();
+ QualType DerivedReturnTy = DerivedMD->getType()
+ ->getAs<FunctionType>()
+ ->getReturnType()
+ .getCanonicalType();
+
+ if (DerivedReturnTy->isDependentType() || BaseReturnTy->isDependentType())
+ return false;
+
+ // Check if return types are identical.
+ if (Context->hasSameType(DerivedReturnTy, BaseReturnTy))
+ return true;
+
+ /// Check if the return types are covariant.
+
+ // Both types must be pointers or references to classes.
+ if (!(BaseReturnTy->isPointerType() && DerivedReturnTy->isPointerType()) &&
+ !(BaseReturnTy->isReferenceType() && DerivedReturnTy->isReferenceType()))
+ return false;
+
+ /// BTy is the class type in return type of BaseMD. For example,
+ /// B* Base::md()
+ /// While BRD is the declaration of B.
+ QualType DTy = DerivedReturnTy->getPointeeType().getCanonicalType();
+ QualType BTy = BaseReturnTy->getPointeeType().getCanonicalType();
+
+ const CXXRecordDecl *DRD = DTy->getAsCXXRecordDecl();
+ const CXXRecordDecl *BRD = BTy->getAsCXXRecordDecl();
+ if (DRD == nullptr || BRD == nullptr)
+ return false;
+
+ if (!DRD->hasDefinition() || !BRD->hasDefinition())
+ return false;
+
+ if (DRD == BRD)
+ return true;
+
+ if (!Context->hasSameUnqualifiedType(DTy, BTy)) {
+ // Begin checking whether the conversion from D to B is valid.
+ CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
+ /*DetectVirtual=*/false);
+
+ // Check whether D is derived from B, and fill in a CXXBasePaths object.
+ if (!DRD->isDerivedFrom(BRD, Paths))
+ return false;
+
+ // Check ambiguity.
+ if (Paths.isAmbiguous(Context->getCanonicalType(BTy).getUnqualifiedType()))
+ return false;
+
+ // Check accessibility.
+ // FIXME: We currently only support checking if B is accessible base class
+ // of D, or D is the same class which DerivedMD is in.
+ bool IsItself =
+ DRD->getCanonicalDecl() == DerivedMD->getParent()->getCanonicalDecl();
+ bool HasPublicAccess = false;
+ for (const auto &Path : Paths) {
+ if (Path.Access == AS_public)
+ HasPublicAccess = true;
+ }
+ if (!HasPublicAccess && !IsItself)
+ return false;
+ // End checking conversion from D to B.
+ }
+
+ // Both pointers or references should have the same cv-qualification.
+ if (DerivedReturnTy.getLocalCVRQualifiers() !=
+ BaseReturnTy.getLocalCVRQualifiers())
+ return false;
+
+ // The class type D should have the same cv-qualification as or less
+ // cv-qualification than the class type B.
+ if (DTy.isMoreQualifiedThan(BTy))
+ return false;
+
+ return true;
+}
+
+/// \returns decayed type for arrays and functions.
+static QualType getDecayedType(QualType Type) {
+ if (const auto *Decayed = Type->getAs<DecayedType>())
+ return Decayed->getDecayedType();
+ return Type;
+}
+
+/// \returns true if the param types are the same.
+static bool checkParamTypes(const CXXMethodDecl *BaseMD,
+ const CXXMethodDecl *DerivedMD) {
+ unsigned NumParamA = BaseMD->getNumParams();
+ unsigned NumParamB = DerivedMD->getNumParams();
+ if (NumParamA != NumParamB)
+ return false;
+
+ for (unsigned I = 0; I < NumParamA; I++) {
+ if (getDecayedType(BaseMD->getParamDecl(I)->getType().getCanonicalType()) !=
+ getDecayedType(
+ DerivedMD->getParamDecl(I)->getType().getCanonicalType()))
+ return false;
+ }
+ return true;
+}
+
+/// \returns true if derived method can override base method except for the
+/// name.
+static bool checkOverrideWithoutName(const ASTContext *Context,
+ const CXXMethodDecl *BaseMD,
+ const CXXMethodDecl *DerivedMD) {
+ if (BaseMD->isStatic() != DerivedMD->isStatic())
+ return false;
+
+ if (BaseMD->getType() == DerivedMD->getType())
+ return true;
+
+ // Now the function types are not identical. Then check if the return types
+ // are covariant and if the param types are the same.
+ if (!checkOverridingFunctionReturnType(Context, BaseMD, DerivedMD))
+ return false;
+ return checkParamTypes(BaseMD, DerivedMD);
+}
+
+/// Check whether BaseMD overrides DerivedMD.
+///
+/// Prerequisite: the class which BaseMD is in should be a base class of that
+/// DerivedMD is in.
+static bool checkOverrideByDerivedMethod(const CXXMethodDecl *BaseMD,
+ const CXXMethodDecl *DerivedMD) {
+ for (CXXMethodDecl::method_iterator I = DerivedMD->begin_overridden_methods(),
+ E = DerivedMD->end_overridden_methods();
+ I != E; ++I) {
+ const CXXMethodDecl *OverriddenMD = *I;
+ if (BaseMD->getCanonicalDecl() == OverriddenMD->getCanonicalDecl())
+ return true;
+ }
+
+ return false;
+}
+
+bool VirtualNearMissCheck::isPossibleToBeOverridden(
+ const CXXMethodDecl *BaseMD) {
+ auto Iter = PossibleMap.find(BaseMD);
+ if (Iter != PossibleMap.end())
+ return Iter->second;
+
+ bool IsPossible = !BaseMD->isImplicit() && !isa<CXXConstructorDecl>(BaseMD) &&
+ !isa<CXXDestructorDecl>(BaseMD) && BaseMD->isVirtual() &&
+ !BaseMD->isOverloadedOperator() &&
+ !isa<CXXConversionDecl>(BaseMD);
+ PossibleMap[BaseMD] = IsPossible;
+ return IsPossible;
+}
+
+bool VirtualNearMissCheck::isOverriddenByDerivedClass(
+ const CXXMethodDecl *BaseMD, const CXXRecordDecl *DerivedRD) {
+ auto Key = std::make_pair(BaseMD, DerivedRD);
+ auto Iter = OverriddenMap.find(Key);
+ if (Iter != OverriddenMap.end())
+ return Iter->second;
+
+ bool IsOverridden = false;
+ for (const CXXMethodDecl *DerivedMD : DerivedRD->methods()) {
+ if (!isOverrideMethod(DerivedMD))
+ continue;
+
+ if (checkOverrideByDerivedMethod(BaseMD, DerivedMD)) {
+ IsOverridden = true;
+ break;
+ }
+ }
+ OverriddenMap[Key] = IsOverridden;
+ return IsOverridden;
+}
+
+void VirtualNearMissCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ Finder->addMatcher(
+ cxxMethodDecl(
+ unless(anyOf(isOverride(), isImplicit(), cxxConstructorDecl(),
+ cxxDestructorDecl(), cxxConversionDecl(), isStatic(),
+ isOverloadedOperator())))
+ .bind("method"),
+ this);
+}
+
+void VirtualNearMissCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *DerivedMD = Result.Nodes.getNodeAs<CXXMethodDecl>("method");
+ assert(DerivedMD);
+
+ const ASTContext *Context = Result.Context;
+
+ const auto *DerivedRD = DerivedMD->getParent()->getDefinition();
+ assert(DerivedRD);
+
+ for (const auto &BaseSpec : DerivedRD->bases()) {
+ if (const auto *BaseRD = BaseSpec.getType()->getAsCXXRecordDecl()) {
+ for (const auto *BaseMD : BaseRD->methods()) {
+ if (!isPossibleToBeOverridden(BaseMD))
+ continue;
+
+ if (isOverriddenByDerivedClass(BaseMD, DerivedRD))
+ continue;
+
+ unsigned EditDistance = BaseMD->getName().edit_distance(
+ DerivedMD->getName(), EditDistanceThreshold);
+ if (EditDistance > 0 && EditDistance <= EditDistanceThreshold) {
+ if (checkOverrideWithoutName(Context, BaseMD, DerivedMD)) {
+ // A "virtual near miss" is found.
+ auto Range = CharSourceRange::getTokenRange(
+ SourceRange(DerivedMD->getLocation()));
+
+ bool ApplyFix = !BaseMD->isTemplateInstantiation() &&
+ !DerivedMD->isTemplateInstantiation();
+ auto Diag =
+ diag(DerivedMD->getLocStart(),
+ "method '%0' has a similar name and the same signature as "
+ "virtual method '%1'; did you mean to override it?")
+ << DerivedMD->getQualifiedNameAsString()
+ << BaseMD->getQualifiedNameAsString();
+ if (ApplyFix)
+ Diag << FixItHint::CreateReplacement(Range, BaseMD->getName());
+ }
+ }
+ }
+ }
+ }
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tools-extra/clang-tidy/bugprone/VirtualNearMissCheck.h b/clang-tools-extra/clang-tidy/bugprone/VirtualNearMissCheck.h
new file mode 100644
index 0000000..ea1e256
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/VirtualNearMissCheck.h
@@ -0,0 +1,65 @@
+//===--- VirtualNearMissCheck.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_BUGPRONE_VIRTUAL_NEAR_MISS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_VIRTUAL_NEAR_MISS_H
+
+#include "../ClangTidy.h"
+#include "llvm/ADT/DenseMap.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// \brief Checks for near miss of virtual methods.
+///
+/// For a method in a derived class, this check looks for virtual method with a
+/// very similar name and an identical signature defined in a base class.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-virtual-near-miss.html
+class VirtualNearMissCheck : public ClangTidyCheck {
+public:
+ VirtualNearMissCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ /// Check if the given method is possible to be overridden by some other
+ /// method. Operators and destructors are excluded.
+ ///
+ /// Results are memoized in PossibleMap.
+ bool isPossibleToBeOverridden(const CXXMethodDecl *BaseMD);
+
+ /// Check if the given base method is overridden by some methods in the given
+ /// derived class.
+ ///
+ /// Results are memoized in OverriddenMap.
+ bool isOverriddenByDerivedClass(const CXXMethodDecl *BaseMD,
+ const CXXRecordDecl *DerivedRD);
+
+ /// Key: the unique ID of a method.
+ /// Value: whether the method is possible to be overridden.
+ llvm::DenseMap<const CXXMethodDecl *, bool> PossibleMap;
+
+ /// Key: <unique ID of base method, name of derived class>
+ /// Value: whether the base method is overridden by some method in the derived
+ /// class.
+ llvm::DenseMap<std::pair<const CXXMethodDecl *, const CXXRecordDecl *>, bool>
+ OverriddenMap;
+
+ const unsigned EditDistanceThreshold = 1;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_VIRTUAL_NEAR_MISS_H