Index: clang-tools-extra/trunk/clang-tidy/bugprone/ArgumentCommentCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/ArgumentCommentCheck.h +++ clang-tools-extra/trunk/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 Index: clang-tools-extra/trunk/clang-tidy/bugprone/AssertSideEffectCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/AssertSideEffectCheck.h +++ clang-tools-extra/trunk/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 + +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 AssertMacros; +}; + +} // namespace bugprone +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_ASSERTSIDEEFFECTCHECK_H Index: clang-tools-extra/trunk/clang-tidy/bugprone/AssertSideEffectCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/AssertSideEffectCheck.cpp +++ clang-tools-extra/trunk/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 +#include + +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(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(E)) { + return Op->isAssignmentOp(); + } + + if (const auto *OpCallExpr = dyn_cast(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(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(FuncDecl)) + Result &= !MethodDecl->isConst(); + } + return Result; + } + + return isa(E) || isa(E) || isa(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("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 Index: clang-tools-extra/trunk/clang-tidy/bugprone/BoolPointerImplicitConversionCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/BoolPointerImplicitConversionCheck.h +++ clang-tools-extra/trunk/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 Index: clang-tools-extra/trunk/clang-tidy/bugprone/BoolPointerImplicitConversionCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/BoolPointerImplicitConversionCheck.cpp +++ clang-tools-extra/trunk/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("if"); + auto *Var = Result.Nodes.getNodeAs("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 Index: clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp +++ clang-tools-extra/trunk/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( "bugprone-argument-comment"); + CheckFactories.registerCheck( + "bugprone-assert-side-effect"); + CheckFactories.registerCheck( + "bugprone-bool-pointer-implicit-conversion"); CheckFactories.registerCheck( "bugprone-copy-constructor-init"); CheckFactories.registerCheck( "bugprone-dangling-handle"); + CheckFactories.registerCheck( + "bugprone-fold-init-type"); + CheckFactories.registerCheck( + "bugprone-forward-declaration-namespace"); + CheckFactories.registerCheck( + "bugprone-inaccurate-erase"); CheckFactories.registerCheck( "bugprone-integer-division"); CheckFactories.registerCheck( "bugprone-misplaced-operator-in-strlen-in-alloc"); + CheckFactories.registerCheck( + "bugprone-move-forwarding-reference"); + CheckFactories.registerCheck( + "bugprone-multiple-statement-macro"); CheckFactories.registerCheck( "bugprone-string-constructor"); CheckFactories.registerCheck( "bugprone-suspicious-memset-usage"); CheckFactories.registerCheck( "bugprone-undefined-memory-manipulation"); + CheckFactories.registerCheck( + "bugprone-use-after-move"); + CheckFactories.registerCheck( + "bugprone-virtual-near-miss"); } }; Index: clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt +++ clang-tools-extra/trunk/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 Index: clang-tools-extra/trunk/clang-tidy/bugprone/FoldInitTypeCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/FoldInitTypeCheck.h +++ clang-tools-extra/trunk/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 Index: clang-tools-extra/trunk/clang-tidy/bugprone/FoldInitTypeCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/FoldInitTypeCheck.cpp +++ clang-tools-extra/trunk/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(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("InitType"); + const auto *IterValueType = + Result.Nodes.getNodeAs("IterValueType"); + assert(InitType != nullptr); + assert(IterValueType != nullptr); + + const auto *CallNode = Result.Nodes.getNodeAs("Call"); + assert(CallNode != nullptr); + + doCheck(*IterValueType, *InitType, *Result.Context, *CallNode); + + if (const auto *Iter2ValueType = + Result.Nodes.getNodeAs("Iter2ValueType")) + doCheck(*Iter2ValueType, *InitType, *Result.Context, *CallNode); +} + +} // namespace bugprone +} // namespace tidy +} // namespace clang Index: clang-tools-extra/trunk/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.h +++ clang-tools-extra/trunk/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 +#include + +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> DeclNameToDefinitions; + llvm::StringMap> DeclNameToDeclarations; + llvm::SmallPtrSet FriendTypes; +}; + +} // namespace bugprone +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_FORWARDDECLARATIONNAMESPACECHECK_H Index: clang-tools-extra/trunk/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.cpp +++ clang-tools-extra/trunk/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 +#include + +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("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("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(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 Index: clang-tools-extra/trunk/clang-tidy/bugprone/InaccurateEraseCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/InaccurateEraseCheck.h +++ clang-tools-extra/trunk/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 Index: clang-tools-extra/trunk/clang-tidy/bugprone/InaccurateEraseCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/InaccurateEraseCheck.cpp +++ clang-tools-extra/trunk/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("erase"); + const auto *EndExpr = + Result.Nodes.getNodeAs("end"); + const SourceLocation Loc = MemberCall->getLocStart(); + + FixItHint Hint; + + if (!Loc.isMacroID() && EndExpr) { + const auto *AlgCall = Result.Nodes.getNodeAs("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 Index: clang-tools-extra/trunk/clang-tidy/bugprone/MoveForwardingReferenceCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/MoveForwardingReferenceCheck.h +++ clang-tools-extra/trunk/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 Index: clang-tools-extra/trunk/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp +++ clang-tools-extra/trunk/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 + +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("call-move"); + const auto *UnresolvedLookup = + Result.Nodes.getNodeAs("lookup"); + const auto *ParmVar = Result.Nodes.getNodeAs("parm-var"); + const auto *TypeParmDecl = + Result.Nodes.getNodeAs("type-parm-decl"); + + // Get the FunctionDecl and FunctionTemplateDecl containing the function + // parameter. + const auto *FuncForParam = dyn_cast(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 Index: clang-tools-extra/trunk/clang-tidy/bugprone/MultipleStatementMacroCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/MultipleStatementMacroCheck.h +++ clang-tools-extra/trunk/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 Index: clang-tools-extra/trunk/clang-tidy/bugprone/MultipleStatementMacroCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/MultipleStatementMacroCheck.cpp +++ clang-tools-extra/trunk/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(); + 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>; + +/// \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("inner"); + const auto *Outer = Result.Nodes.getNodeAs("outer"); + const auto *Next = nextStmt(Result, Outer); + if (!Next) + return; + + SourceLocation OuterLoc = Outer->getLocStart(); + if (Result.Nodes.getNodeAs("else")) + OuterLoc = cast(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 Index: clang-tools-extra/trunk/clang-tidy/bugprone/UseAfterMoveCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/UseAfterMoveCheck.h +++ clang-tools-extra/trunk/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 Index: clang-tools-extra/trunk/clang-tidy/bugprone/UseAfterMoveCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/UseAfterMoveCheck.cpp +++ clang-tools-extra/trunk/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 *Uses, + llvm::SmallPtrSetImpl *Reinits); + void getDeclRefs(const CFGBlock *Block, const Decl *MovedVariable, + llvm::SmallPtrSetImpl *DeclRefs); + void getReinits(const CFGBlock *Block, const ValueDecl *MovedVariable, + llvm::SmallPtrSetImpl *Stmts, + llvm::SmallPtrSetImpl *DeclRefs); + + ASTContext *Context; + std::unique_ptr Sequence; + std::unique_ptr BlockMap; + llvm::SmallPtrSet 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 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 Uses; + llvm::SmallPtrSet Reinits; + getUsesAndReinits(Block, MovedVariable, &Uses, &Reinits); + + // Ignore all reinitializations where the move potentially comes after the + // reinit. + llvm::SmallVector 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 *Uses, + llvm::SmallPtrSetImpl *Reinits) { + llvm::SmallPtrSet DeclRefs; + llvm::SmallPtrSet 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 *DeclRefs) { + DeclRefs->clear(); + for (const auto &Elem : *Block) { + Optional S = Elem.getAs(); + if (!S) + continue; + + auto addDeclRefs = [this, Block, + DeclRefs](const ArrayRef Matches) { + for (const auto &Match : Matches) { + const auto *DeclRef = Match.getNodeAs("declref"); + const auto *Operator = Match.getNodeAs("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 *Stmts, + llvm::SmallPtrSetImpl *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 S = Elem.getAs(); + if (!S) + continue; + + SmallVector Matches = + match(findAll(ReinitMatcher), *S->getStmt(), *Context); + + for (const auto &Match : Matches) { + const auto *TheStmt = Match.getNodeAs("reinit"); + const auto *TheDeclRef = Match.getNodeAs("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("containing-lambda"); + const auto *ContainingFunc = + Result.Nodes.getNodeAs("containing-func"); + const auto *CallMove = Result.Nodes.getNodeAs("call-move"); + const auto *MovingCall = Result.Nodes.getNodeAs("moving-call"); + const auto *Arg = Result.Nodes.getNodeAs("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 Index: clang-tools-extra/trunk/clang-tidy/bugprone/VirtualNearMissCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/VirtualNearMissCheck.h +++ clang-tools-extra/trunk/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 PossibleMap; + + /// Key: + /// Value: whether the base method is overridden by some method in the derived + /// class. + llvm::DenseMap, bool> + OverriddenMap; + + const unsigned EditDistanceThreshold = 1; +}; + +} // namespace bugprone +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_VIRTUAL_NEAR_MISS_H Index: clang-tools-extra/trunk/clang-tidy/bugprone/VirtualNearMissCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/VirtualNearMissCheck.cpp +++ clang-tools-extra/trunk/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(); +} + +/// 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() + ->getReturnType() + .getCanonicalType(); + QualType DerivedReturnTy = DerivedMD->getType() + ->getAs() + ->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()) + 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(BaseMD) && + !isa(BaseMD) && BaseMD->isVirtual() && + !BaseMD->isOverloadedOperator() && + !isa(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("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 Index: clang-tools-extra/trunk/clang-tidy/hicpp/HICPPTidyModule.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/hicpp/HICPPTidyModule.cpp +++ clang-tools-extra/trunk/clang-tidy/hicpp/HICPPTidyModule.cpp @@ -10,6 +10,7 @@ #include "../ClangTidy.h" #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" +#include "../bugprone/UseAfterMoveCheck.h" #include "../cppcoreguidelines/NoMallocCheck.h" #include "../cppcoreguidelines/ProBoundsArrayToPointerDecayCheck.h" #include "../cppcoreguidelines/ProTypeMemberInitCheck.h" @@ -22,7 +23,6 @@ #include "../misc/NoexceptMoveConstructorCheck.h" #include "../misc/StaticAssertCheck.h" #include "../misc/UndelegatedConstructor.h" -#include "../misc/UseAfterMoveCheck.h" #include "../modernize/DeprecatedHeadersCheck.h" #include "../modernize/UseAutoCheck.h" #include "../modernize/UseEmplaceCheck.h" @@ -59,7 +59,7 @@ "hicpp-function-size"); CheckFactories.registerCheck( "hicpp-named-parameter"); - CheckFactories.registerCheck( + CheckFactories.registerCheck( "hicpp-invalid-access-moved"); CheckFactories.registerCheck( "hicpp-member-init"); Index: clang-tools-extra/trunk/clang-tidy/misc/AssertSideEffectCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/AssertSideEffectCheck.h +++ clang-tools-extra/trunk/clang-tidy/misc/AssertSideEffectCheck.h @@ -1,52 +0,0 @@ -//===--- 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_MISC_ASSERTSIDEEFFECTCHECK_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ASSERTSIDEEFFECTCHECK_H - -#include "../ClangTidy.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringRef.h" -#include - -namespace clang { -namespace tidy { -namespace misc { - -/// 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 AssertMacros; -}; - -} // namespace misc -} // namespace tidy -} // namespace clang - -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ASSERTSIDEEFFECTCHECK_H Index: clang-tools-extra/trunk/clang-tidy/misc/AssertSideEffectCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/AssertSideEffectCheck.cpp +++ clang-tools-extra/trunk/clang-tidy/misc/AssertSideEffectCheck.cpp @@ -1,127 +0,0 @@ -//===--- 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 -#include - -using namespace clang::ast_matchers; - -namespace clang { -namespace tidy { -namespace misc { - -namespace { - -AST_MATCHER_P(Expr, hasSideEffect, bool, CheckFunctionCalls) { - const Expr *E = &Node; - - if (const auto *Op = dyn_cast(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(E)) { - return Op->isAssignmentOp(); - } - - if (const auto *OpCallExpr = dyn_cast(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(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(FuncDecl)) - Result &= !MethodDecl->isConst(); - } - return Result; - } - - return isa(E) || isa(E) || isa(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("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 misc -} // namespace tidy -} // namespace clang Index: clang-tools-extra/trunk/clang-tidy/misc/BoolPointerImplicitConversionCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/BoolPointerImplicitConversionCheck.h +++ clang-tools-extra/trunk/clang-tidy/misc/BoolPointerImplicitConversionCheck.h @@ -1,42 +0,0 @@ -//===--- 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_MISC_BOOLPOINTERIMPLICITCONVERSIONCHECK_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_BOOLPOINTERIMPLICITCONVERSIONCHECK_H - -#include "../ClangTidy.h" - -namespace clang { -namespace tidy { -namespace misc { - -/// 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 misc -} // namespace tidy -} // namespace clang - -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_BOOLPOINTERIMPLICITCONVERSIONCHECK_H Index: clang-tools-extra/trunk/clang-tidy/misc/BoolPointerImplicitConversionCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/BoolPointerImplicitConversionCheck.cpp +++ clang-tools-extra/trunk/clang-tidy/misc/BoolPointerImplicitConversionCheck.cpp @@ -1,73 +0,0 @@ -//===--- 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 misc { - -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("if"); - auto *Var = Result.Nodes.getNodeAs("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 misc -} // namespace tidy -} // namespace clang Index: clang-tools-extra/trunk/clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/CMakeLists.txt +++ clang-tools-extra/trunk/clang-tidy/misc/CMakeLists.txt @@ -1,16 +1,11 @@ set(LLVM_LINK_COMPONENTS support) add_clang_library(clangTidyMiscModule - AssertSideEffectCheck.cpp ForwardingReferenceOverloadCheck.cpp LambdaFunctionNameCheck.cpp MisplacedConstCheck.cpp UnconventionalAssignOperatorCheck.cpp - BoolPointerImplicitConversionCheck.cpp DefinitionsInHeadersCheck.cpp - FoldInitTypeCheck.cpp - ForwardDeclarationNamespaceCheck.cpp - InaccurateEraseCheck.cpp IncorrectRoundings.cpp InefficientAlgorithmCheck.cpp MacroParenthesesCheck.cpp @@ -19,8 +14,6 @@ MisplacedWideningCastCheck.cpp MoveConstantArgumentCheck.cpp MoveConstructorInitCheck.cpp - MoveForwardingReferenceCheck.cpp - MultipleStatementMacroCheck.cpp NewDeleteOverloadsCheck.cpp NoexceptMoveConstructorCheck.cpp NonCopyableObjects.cpp @@ -43,8 +36,6 @@ UnusedParametersCheck.cpp UnusedRAIICheck.cpp UnusedUsingDeclsCheck.cpp - UseAfterMoveCheck.cpp - VirtualNearMissCheck.cpp LINK_LIBS clangAnalysis Index: clang-tools-extra/trunk/clang-tidy/misc/FoldInitTypeCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/FoldInitTypeCheck.h +++ clang-tools-extra/trunk/clang-tidy/misc/FoldInitTypeCheck.h @@ -1,44 +0,0 @@ -//===--- 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_MISC_FOLD_INIT_TYPE_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FOLD_INIT_TYPE_H - -#include "../ClangTidy.h" - -namespace clang { -namespace tidy { -namespace misc { - -/// 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/misc-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 misc -} // namespace tidy -} // namespace clang - -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FOLD_INIT_TYPE_H Index: clang-tools-extra/trunk/clang-tidy/misc/FoldInitTypeCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/FoldInitTypeCheck.cpp +++ clang-tools-extra/trunk/clang-tidy/misc/FoldInitTypeCheck.cpp @@ -1,140 +0,0 @@ -//===--- 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 misc { - -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(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("InitType"); - const auto *IterValueType = - Result.Nodes.getNodeAs("IterValueType"); - assert(InitType != nullptr); - assert(IterValueType != nullptr); - - const auto *CallNode = Result.Nodes.getNodeAs("Call"); - assert(CallNode != nullptr); - - doCheck(*IterValueType, *InitType, *Result.Context, *CallNode); - - if (const auto *Iter2ValueType = - Result.Nodes.getNodeAs("Iter2ValueType")) - doCheck(*Iter2ValueType, *InitType, *Result.Context, *CallNode); -} - -} // namespace misc -} // namespace tidy -} // namespace clang Index: clang-tools-extra/trunk/clang-tidy/misc/ForwardDeclarationNamespaceCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/ForwardDeclarationNamespaceCheck.h +++ clang-tools-extra/trunk/clang-tidy/misc/ForwardDeclarationNamespaceCheck.h @@ -1,59 +0,0 @@ -//===--- 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_MISC_FORWARDDECLARATIONNAMESPACECHECK_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORWARDDECLARATIONNAMESPACECHECK_H - -#include "../ClangTidy.h" -#include "llvm/ADT/SmallPtrSet.h" -#include -#include - -namespace clang { -namespace tidy { -namespace misc { - -/// 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/misc-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> DeclNameToDefinitions; - llvm::StringMap> DeclNameToDeclarations; - llvm::SmallPtrSet FriendTypes; -}; - -} // namespace misc -} // namespace tidy -} // namespace clang - -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORWARDDECLARATIONNAMESPACECHECK_H Index: clang-tools-extra/trunk/clang-tidy/misc/ForwardDeclarationNamespaceCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/ForwardDeclarationNamespaceCheck.cpp +++ clang-tools-extra/trunk/clang-tidy/misc/ForwardDeclarationNamespaceCheck.cpp @@ -1,174 +0,0 @@ -//===--- 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 -#include - -using namespace clang::ast_matchers; - -namespace clang { -namespace tidy { -namespace misc { - -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("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("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(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 misc -} // namespace tidy -} // namespace clang Index: clang-tools-extra/trunk/clang-tidy/misc/InaccurateEraseCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/InaccurateEraseCheck.h +++ clang-tools-extra/trunk/clang-tidy/misc/InaccurateEraseCheck.h @@ -1,38 +0,0 @@ -//===--- 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_MISC_INACCURATEERASECHECK_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INACCURATEERASECHECK_H - -#include "../ClangTidy.h" - -namespace clang { -namespace tidy { -namespace misc { - -/// 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 misc -} // namespace tidy -} // namespace clang - -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INACCURATEERASECHECK_H Index: clang-tools-extra/trunk/clang-tidy/misc/InaccurateEraseCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/InaccurateEraseCheck.cpp +++ clang-tools-extra/trunk/clang-tidy/misc/InaccurateEraseCheck.cpp @@ -1,81 +0,0 @@ -//===--- 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 misc { - -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("erase"); - const auto *EndExpr = - Result.Nodes.getNodeAs("end"); - const SourceLocation Loc = MemberCall->getLocStart(); - - FixItHint Hint; - - if (!Loc.isMacroID() && EndExpr) { - const auto *AlgCall = Result.Nodes.getNodeAs("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 misc -} // namespace tidy -} // namespace clang Index: clang-tools-extra/trunk/clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/MiscTidyModule.cpp +++ clang-tools-extra/trunk/clang-tidy/misc/MiscTidyModule.cpp @@ -10,13 +10,8 @@ #include "../ClangTidy.h" #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" -#include "AssertSideEffectCheck.h" -#include "BoolPointerImplicitConversionCheck.h" #include "DefinitionsInHeadersCheck.h" -#include "FoldInitTypeCheck.h" -#include "ForwardDeclarationNamespaceCheck.h" #include "ForwardingReferenceOverloadCheck.h" -#include "InaccurateEraseCheck.h" #include "IncorrectRoundings.h" #include "InefficientAlgorithmCheck.h" #include "LambdaFunctionNameCheck.h" @@ -26,8 +21,6 @@ #include "MisplacedWideningCastCheck.h" #include "MoveConstantArgumentCheck.h" #include "MoveConstructorInitCheck.h" -#include "MoveForwardingReferenceCheck.h" -#include "MultipleStatementMacroCheck.h" #include "NewDeleteOverloadsCheck.h" #include "NoexceptMoveConstructorCheck.h" #include "NonCopyableObjects.h" @@ -51,8 +44,6 @@ #include "UnusedParametersCheck.h" #include "UnusedRAIICheck.h" #include "UnusedUsingDeclsCheck.h" -#include "UseAfterMoveCheck.h" -#include "VirtualNearMissCheck.h" namespace clang { namespace tidy { @@ -61,8 +52,6 @@ class MiscModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { - CheckFactories.registerCheck( - "misc-assert-side-effect"); CheckFactories.registerCheck( "misc-forwarding-reference-overload"); CheckFactories.registerCheck( @@ -70,14 +59,8 @@ CheckFactories.registerCheck("misc-misplaced-const"); CheckFactories.registerCheck( "misc-unconventional-assign-operator"); - CheckFactories.registerCheck( - "misc-bool-pointer-implicit-conversion"); CheckFactories.registerCheck( "misc-definitions-in-headers"); - CheckFactories.registerCheck("misc-fold-init-type"); - CheckFactories.registerCheck( - "misc-forward-declaration-namespace"); - CheckFactories.registerCheck("misc-inaccurate-erase"); CheckFactories.registerCheck( "misc-incorrect-roundings"); CheckFactories.registerCheck( @@ -92,10 +75,6 @@ "misc-move-const-arg"); CheckFactories.registerCheck( "misc-move-constructor-init"); - CheckFactories.registerCheck( - "misc-move-forwarding-reference"); - CheckFactories.registerCheck( - "misc-multiple-statement-macro"); CheckFactories.registerCheck( "misc-new-delete-overloads"); CheckFactories.registerCheck( @@ -136,9 +115,6 @@ CheckFactories.registerCheck("misc-unused-raii"); CheckFactories.registerCheck( "misc-unused-using-decls"); - CheckFactories.registerCheck("misc-use-after-move"); - CheckFactories.registerCheck( - "misc-virtual-near-miss"); } }; Index: clang-tools-extra/trunk/clang-tidy/misc/MoveForwardingReferenceCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/MoveForwardingReferenceCheck.h +++ clang-tools-extra/trunk/clang-tidy/misc/MoveForwardingReferenceCheck.h @@ -1,49 +0,0 @@ -//===--- 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_MISC_MOVEFORWARDINGREFERENCECHECK_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MOVEFORWARDINGREFERENCECHECK_H - -#include "../ClangTidy.h" - -namespace clang { -namespace tidy { -namespace misc { - -/// 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/misc-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 misc -} // namespace tidy -} // namespace clang - -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MOVEFORWARDINGREFERENCECHECK_H Index: clang-tools-extra/trunk/clang-tidy/misc/MoveForwardingReferenceCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/MoveForwardingReferenceCheck.cpp +++ clang-tools-extra/trunk/clang-tidy/misc/MoveForwardingReferenceCheck.cpp @@ -1,133 +0,0 @@ -//===--- 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 - -using namespace clang::ast_matchers; - -namespace clang { -namespace tidy { -namespace misc { - -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("call-move"); - const auto *UnresolvedLookup = - Result.Nodes.getNodeAs("lookup"); - const auto *ParmVar = Result.Nodes.getNodeAs("parm-var"); - const auto *TypeParmDecl = - Result.Nodes.getNodeAs("type-parm-decl"); - - // Get the FunctionDecl and FunctionTemplateDecl containing the function - // parameter. - const auto *FuncForParam = dyn_cast(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 misc -} // namespace tidy -} // namespace clang Index: clang-tools-extra/trunk/clang-tidy/misc/MultipleStatementMacroCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/MultipleStatementMacroCheck.h +++ clang-tools-extra/trunk/clang-tidy/misc/MultipleStatementMacroCheck.h @@ -1,37 +0,0 @@ -//===--- 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_MISC_MULTIPLE_STATEMENT_MACRO_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MULTIPLE_STATEMENT_MACRO_H - -#include "../ClangTidy.h" - -namespace clang { -namespace tidy { -namespace misc { - -/// 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/misc-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 misc -} // namespace tidy -} // namespace clang - -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MULTIPLE_STATEMENT_MACRO_H Index: clang-tools-extra/trunk/clang-tidy/misc/MultipleStatementMacroCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/MultipleStatementMacroCheck.cpp +++ clang-tools-extra/trunk/clang-tidy/misc/MultipleStatementMacroCheck.cpp @@ -1,106 +0,0 @@ -//===--- 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 misc { - -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(); - 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>; - -/// \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("inner"); - const auto *Outer = Result.Nodes.getNodeAs("outer"); - const auto *Next = nextStmt(Result, Outer); - if (!Next) - return; - - SourceLocation OuterLoc = Outer->getLocStart(); - if (Result.Nodes.getNodeAs("else")) - OuterLoc = cast(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 misc -} // namespace tidy -} // namespace clang Index: clang-tools-extra/trunk/clang-tidy/misc/UseAfterMoveCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/UseAfterMoveCheck.h +++ clang-tools-extra/trunk/clang-tidy/misc/UseAfterMoveCheck.h @@ -1,36 +0,0 @@ -//===--- 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_MISC_USEAFTERMOVECHECK_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_USEAFTERMOVECHECK_H - -#include "../ClangTidy.h" - -namespace clang { -namespace tidy { -namespace misc { - -/// 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/misc-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 misc -} // namespace tidy -} // namespace clang - -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_USEAFTERMOVECHECK_H Index: clang-tools-extra/trunk/clang-tidy/misc/UseAfterMoveCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/UseAfterMoveCheck.cpp +++ clang-tools-extra/trunk/clang-tidy/misc/UseAfterMoveCheck.cpp @@ -1,434 +0,0 @@ -//===--- 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 misc { - -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 *Uses, - llvm::SmallPtrSetImpl *Reinits); - void getDeclRefs(const CFGBlock *Block, const Decl *MovedVariable, - llvm::SmallPtrSetImpl *DeclRefs); - void getReinits(const CFGBlock *Block, const ValueDecl *MovedVariable, - llvm::SmallPtrSetImpl *Stmts, - llvm::SmallPtrSetImpl *DeclRefs); - - ASTContext *Context; - std::unique_ptr Sequence; - std::unique_ptr BlockMap; - llvm::SmallPtrSet 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 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 Uses; - llvm::SmallPtrSet Reinits; - getUsesAndReinits(Block, MovedVariable, &Uses, &Reinits); - - // Ignore all reinitializations where the move potentially comes after the - // reinit. - llvm::SmallVector 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 *Uses, - llvm::SmallPtrSetImpl *Reinits) { - llvm::SmallPtrSet DeclRefs; - llvm::SmallPtrSet 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 *DeclRefs) { - DeclRefs->clear(); - for (const auto &Elem : *Block) { - Optional S = Elem.getAs(); - if (!S) - continue; - - auto addDeclRefs = [this, Block, - DeclRefs](const ArrayRef Matches) { - for (const auto &Match : Matches) { - const auto *DeclRef = Match.getNodeAs("declref"); - const auto *Operator = Match.getNodeAs("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 *Stmts, - llvm::SmallPtrSetImpl *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 S = Elem.getAs(); - if (!S) - continue; - - SmallVector Matches = - match(findAll(ReinitMatcher), *S->getStmt(), *Context); - - for (const auto &Match : Matches) { - const auto *TheStmt = Match.getNodeAs("reinit"); - const auto *TheDeclRef = Match.getNodeAs("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("containing-lambda"); - const auto *ContainingFunc = - Result.Nodes.getNodeAs("containing-func"); - const auto *CallMove = Result.Nodes.getNodeAs("call-move"); - const auto *MovingCall = Result.Nodes.getNodeAs("moving-call"); - const auto *Arg = Result.Nodes.getNodeAs("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 misc -} // namespace tidy -} // namespace clang Index: clang-tools-extra/trunk/clang-tidy/misc/VirtualNearMissCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/VirtualNearMissCheck.h +++ clang-tools-extra/trunk/clang-tidy/misc/VirtualNearMissCheck.h @@ -1,65 +0,0 @@ -//===--- 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_MISC_VIRTUAL_NEAR_MISS_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_VIRTUAL_NEAR_MISS_H - -#include "../ClangTidy.h" -#include "llvm/ADT/DenseMap.h" - -namespace clang { -namespace tidy { -namespace misc { - -/// \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/misc-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 PossibleMap; - - /// Key: - /// Value: whether the base method is overridden by some method in the derived - /// class. - llvm::DenseMap, bool> - OverriddenMap; - - const unsigned EditDistanceThreshold = 1; -}; - -} // namespace misc -} // namespace tidy -} // namespace clang - -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_VIRTUAL_NEAR_MISS_H Index: clang-tools-extra/trunk/clang-tidy/misc/VirtualNearMissCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/VirtualNearMissCheck.cpp +++ clang-tools-extra/trunk/clang-tidy/misc/VirtualNearMissCheck.cpp @@ -1,274 +0,0 @@ -//===--- 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 misc { - -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(); -} - -/// 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() - ->getReturnType() - .getCanonicalType(); - QualType DerivedReturnTy = DerivedMD->getType() - ->getAs() - ->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()) - 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(BaseMD) && - !isa(BaseMD) && BaseMD->isVirtual() && - !BaseMD->isOverloadedOperator() && - !isa(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("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 misc -} // namespace tidy -} // namespace clang Index: clang-tools-extra/trunk/docs/ReleaseNotes.rst =================================================================== --- clang-tools-extra/trunk/docs/ReleaseNotes.rst +++ clang-tools-extra/trunk/docs/ReleaseNotes.rst @@ -57,6 +57,33 @@ Improvements to clang-tidy -------------------------- +- The 'misc-virtual-near-miss' check was renamed to `bugprone-virtual-near-miss + `_ + +- The 'misc-use-after-move' check was renamed to `bugprone-use-after-move + `_ + +- The 'misc-multiple-statement-macro' check was renamed to `bugprone-multiple-statement-macro + `_ + +- The 'misc-move-forwarding-reference' check was renamed to `bugprone-move-forwarding-reference + `_ + +- The 'misc-inaccurate-erase' check was renamed to `bugprone-inaccurate-erase + `_ + +- The 'misc-forward-declaration-namespace' check was renamed to `bugprone-forward-declaration-namespace + `_ + +- The 'misc-fold-init-type' check was renamed to `bugprone-fold-init-type + `_ + +- The 'misc-bool-pointer-implicit-conversion' check was renamed to `bugprone-bool-pointer-implicit-conversion + `_ + +- The 'misc-assert-side-effect' check was renamed to `bugprone-assert-side-effect + `_ + - The 'misc-dangling-handle' check was renamed to `bugprone-dangling-handle `_ Index: clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-assert-side-effect.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-assert-side-effect.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-assert-side-effect.rst @@ -0,0 +1,23 @@ +.. title:: clang-tidy - bugprone-assert-side-effect + +bugprone-assert-side-effect +=========================== + +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. + +Options +------- + +.. option:: AssertMacros + + A comma-separated list of the names of assert macros to be checked. + +.. option:: 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. Index: clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-bool-pointer-implicit-conversion.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-bool-pointer-implicit-conversion.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-bool-pointer-implicit-conversion.rst @@ -0,0 +1,16 @@ +.. title:: clang-tidy - bugprone-bool-pointer-implicit-conversion + +bugprone-bool-pointer-implicit-conversion +========================================= + +Checks for conditions based on implicit conversion from a ``bool`` pointer to +``bool``. + +Example: + +.. code-block:: c++ + + bool *p; + if (p) { + // Never used in a pointer-specific way. + } Index: clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-fold-init-type.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-fold-init-type.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-fold-init-type.rst @@ -0,0 +1,27 @@ +.. title:: clang-tidy - bugprone-fold-init-type + +bugprone-fold-init-type +======================= + +The check flags type mismatches in +`folds `_ +like ``std::accumulate`` that might result in loss of precision. +``std::accumulate`` folds an input range into an initial value using the type of +the latter, with ``operator+`` by default. This can cause loss of precision +through: + +- Truncation: The following code uses a floating point range and an int + initial value, so trucation wil happen at every application of ``operator+`` + and the result will be `0`, which might not be what the user expected. + +.. code-block:: c++ + + auto a = {0.5f, 0.5f, 0.5f, 0.5f}; + return std::accumulate(std::begin(a), std::end(a), 0); + +- Overflow: The following code also returns `0`. + +.. code-block:: c++ + + auto a = {65536LL * 65536 * 65536}; + return std::accumulate(std::begin(a), std::end(a), 0); Index: clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-forward-declaration-namespace.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-forward-declaration-namespace.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-forward-declaration-namespace.rst @@ -0,0 +1,20 @@ +.. title:: clang-tidy - bugprone-forward-declaration-namespace + +bugprone-forward-declaration-namespace +====================================== + +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 existing, which could indicate that +the forward declaration is in a potentially wrong namespace. + +.. code-block:: c++ + + 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::' + +This check can only generate warnings, but it can't suggest a fix at this point. Index: clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-inaccurate-erase.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-inaccurate-erase.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-inaccurate-erase.rst @@ -0,0 +1,13 @@ +.. title:: clang-tidy - bugprone-inaccurate-erase + +bugprone-inaccurate-erase +========================= + + +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. Index: clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-move-forwarding-reference.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-move-forwarding-reference.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-move-forwarding-reference.rst @@ -0,0 +1,60 @@ +.. title:: clang-tidy - bugprone-move-forwarding-reference + +bugprone-move-forwarding-reference +================================== + +Warns if ``std::move`` is called on a forwarding reference, for example: + + .. code-block:: c++ + + template + void foo(T&& t) { + bar(std::move(t)); + } + +`Forwarding references +`_ should +typically be passed to ``std::forward`` instead of ``std::move``, and this is +the fix that will be suggested. + +(A forwarding reference is an rvalue reference of a type that is a deduced +function template argument.) + +In this example, the suggested fix would be + + .. code-block:: c++ + + bar(std::forward(t)); + +Background +---------- + +Code like the example above is sometimes written with the expectation that +``T&&`` will always end up being an rvalue reference, no matter what type is +deduced for ``T``, and that it is therefore not possible to pass an lvalue to +``foo()``. However, this is not true. Consider this example: + + .. code-block:: c++ + + std::string s = "Hello, world"; + foo(s); + +This code compiles and, after the call to ``foo()``, ``s`` is left in an +indeterminate state because it has been moved from. This may be surprising to +the caller of ``foo()`` because no ``std::move`` was used when calling +``foo()``. + +The reason for this behavior lies in the special rule for template argument +deduction on function templates like ``foo()`` -- i.e. on function templates +that take an rvalue reference argument of a type that is a deduced function +template argument. (See section [temp.deduct.call]/3 in the C++11 standard.) + +If ``foo()`` is called on an lvalue (as in the example above), then ``T`` is +deduced to be an lvalue reference. In the example, ``T`` is deduced to be +``std::string &``. The type of the argument ``t`` therefore becomes +``std::string& &&``; by the reference collapsing rules, this collapses to +``std::string&``. + +This means that the ``foo(s)`` call passes ``s`` as an lvalue reference, and +``foo()`` ends up moving ``s`` and thereby placing it into an indeterminate +state. Index: clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-multiple-statement-macro.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-multiple-statement-macro.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-multiple-statement-macro.rst @@ -0,0 +1,16 @@ +.. title:: clang-tidy - bugprone-multiple-statement-macro + +bugprone-multiple-statement-macro +================================= + +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. + +Example: + +.. code-block:: c++ + + #define INCREMENT_TWO(x, y) (x)++; (y)++ + if (do_increment) + INCREMENT_TWO(a, b); // (b)++ will be executed unconditionally. Index: clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-use-after-move.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-use-after-move.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-use-after-move.rst @@ -0,0 +1,203 @@ +.. title:: clang-tidy - bugprone-use-after-move + +bugprone-use-after-move +======================= + +Warns if an object is used after it has been moved, for example: + + .. code-block:: c++ + + std::string str = "Hello, world!\n"; + std::vector messages; + messages.emplace_back(std::move(str)); + std::cout << str; + +The last line will trigger a warning that ``str`` is used after it has been +moved. + +The check does not trigger a warning if the object is reinitialized after the +move and before the use. For example, no warning will be output for this code: + + .. code-block:: c++ + + messages.emplace_back(std::move(str)); + str = "Greetings, stranger!\n"; + std::cout << str; + +The check takes control flow into account. A warning is only emitted if the use +can be reached from the move. This means that the following code does not +produce a warning: + + .. code-block:: c++ + + if (condition) { + messages.emplace_back(std::move(str)); + } else { + std::cout << str; + } + +On the other hand, the following code does produce a warning: + + .. code-block:: c++ + + for (int i = 0; i < 10; ++i) { + std::cout << str; + messages.emplace_back(std::move(str)); + } + +(The use-after-move happens on the second iteration of the loop.) + +In some cases, the check may not be able to detect that two branches are +mutually exclusive. For example (assuming that ``i`` is an int): + + .. code-block:: c++ + + if (i == 1) { + messages.emplace_back(std::move(str)); + } + if (i == 2) { + std::cout << str; + } + +In this case, the check will erroneously produce a warning, even though it is +not possible for both the move and the use to be executed. + +An erroneous warning can be silenced by reinitializing the object after the +move: + + .. code-block:: c++ + + if (i == 1) { + messages.emplace_back(std::move(str)); + str = ""; + } + if (i == 2) { + std::cout << str; + } + +Subsections below explain more precisely what exactly the check considers to be +a move, use, and reinitialization. + +Unsequenced moves, uses, and reinitializations +---------------------------------------------- + +In many cases, C++ does not make any guarantees about the order in which +sub-expressions of a statement are evaluated. This means that in code like the +following, it is not guaranteed whether the use will happen before or after the +move: + + .. code-block:: c++ + + void f(int i, std::vector v); + std::vector v = { 1, 2, 3 }; + f(v[1], std::move(v)); + +In this kind of situation, the check will note that the use and move are +unsequenced. + +The check will also take sequencing rules into account when reinitializations +occur in the same statement as moves or uses. A reinitialization is only +considered to reinitialize a variable if it is guaranteed to be evaluated after +the move and before the use. + +Move +---- + +The check currently only considers calls of ``std::move`` on local variables or +function parameters. It does not check moves of member variables or global +variables. + +Any call of ``std::move`` on a variable is considered to cause a move of that +variable, even if the result of ``std::move`` is not passed to an rvalue +reference parameter. + +This means that the check will flag a use-after-move even on a type that does +not define a move constructor or move assignment operator. This is intentional. +Developers may use ``std::move`` on such a type in the expectation that the type +will add move semantics in the future. If such a ``std::move`` has the potential +to cause a use-after-move, we want to warn about it even if the type does not +implement move semantics yet. + +Furthermore, if the result of ``std::move`` *is* passed to an rvalue reference +parameter, this will always be considered to cause a move, even if the function +that consumes this parameter does not move from it, or if it does so only +conditionally. For example, in the following situation, the check will assume +that a move always takes place: + + .. code-block:: c++ + + std::vector messages; + void f(std::string &&str) { + // Only remember the message if it isn't empty. + if (!str.empty()) { + messages.emplace_back(std::move(str)); + } + } + std::string str = ""; + f(std::move(str)); + +The check will assume that the last line causes a move, even though, in this +particular case, it does not. Again, this is intentional. + +When analyzing the order in which moves, uses and reinitializations happen (see +section `Unsequenced moves, uses, and reinitializations`_), the move is assumed +to occur in whichever function the result of the ``std::move`` is passed to. + +Use +--- + +Any occurrence of the moved variable that is not a reinitialization (see below) +is considered to be a use. + +An exception to this are objects of type ``std::unique_ptr``, +``std::shared_ptr`` and ``std::weak_ptr``, which have defined move behavior +(objects of these classes are guaranteed to be empty after they have been moved +from). Therefore, an object of these classes will only be considered to be used +if it is dereferenced, i.e. if ``operator*``, ``operator->`` or ``operator[]`` +(in the case of ``std::unique_ptr``) is called on it. + +If multiple uses occur after a move, only the first of these is flagged. + +Reinitialization +---------------- + +The check considers a variable to be reinitialized in the following cases: + + - The variable occurs on the left-hand side of an assignment. + + - The variable is passed to a function as a non-const pointer or non-const + lvalue reference. (It is assumed that the variable may be an out-parameter + for the function.) + + - ``clear()`` or ``assign()`` is called on the variable and the variable is of + one of the standard container types ``basic_string``, ``vector``, ``deque``, + ``forward_list``, ``list``, ``set``, ``map``, ``multiset``, ``multimap``, + ``unordered_set``, ``unordered_map``, ``unordered_multiset``, + ``unordered_multimap``. + + - ``reset()`` is called on the variable and the variable is of type + ``std::unique_ptr``, ``std::shared_ptr`` or ``std::weak_ptr``. + +If the variable in question is a struct and an individual member variable of +that struct is written to, the check does not consider this to be a +reinitialization -- even if, eventually, all member variables of the struct are +written to. For example: + + .. code-block:: c++ + + struct S { + std::string str; + int i; + }; + S s = { "Hello, world!\n", 42 }; + S s_other = std::move(s); + s.str = "Lorem ipsum"; + s.i = 99; + +The check will not consider ``s`` to be reinitialized after the last line; +instead, the line that assigns to ``s.str`` will be flagged as a use-after-move. +This is intentional as this pattern of reinitializing a struct is error-prone. +For example, if an additional member variable is added to ``S``, it is easy to +forget to add the reinitialization for this additional member. Instead, it is +safer to assign to the entire struct in one go, and this will also avoid the +use-after-move warning. Index: clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-virtual-near-miss.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-virtual-near-miss.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-virtual-near-miss.rst @@ -0,0 +1,20 @@ +.. title:: clang-tidy - bugprone-virtual-near-miss + +bugprone-virtual-near-miss +========================== + +Warn if a function is a near miss (ie. the name is very similar and the function +signiture is the same) to a virtual function from a base class. + +Example: + +.. code-block:: c++ + + struct Base { + virtual void func(); + }; + + struct Derived : Base { + virtual funk(); + // warning: 'Derived::funk' has a similar name and the same signature as virtual method 'Base::func'; did you mean to override it? + }; Index: clang-tools-extra/trunk/docs/clang-tidy/checks/hicpp-invalid-access-moved.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/hicpp-invalid-access-moved.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/hicpp-invalid-access-moved.rst @@ -1,10 +1,10 @@ .. title:: clang-tidy - hicpp-invalid-access-moved .. meta:: - :http-equiv=refresh: 5;URL=misc-use-after-move.html + :http-equiv=refresh: 5;URL=bugprone-use-after-move.html hicpp-invalid-access-moved ========================== -This check is an alias for `misc-use-after-move `_. +This check is an alias for `bugprone-use-after-move `_. Implements parts of the `rule 8.4.1 `_ to check if moved-from objects are accessed. Index: clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst @@ -18,13 +18,22 @@ android-cloexec-socket boost-use-to-string bugprone-argument-comment + bugprone-assert-side-effect + bugprone-bool-pointer-implicit-conversion bugprone-copy-constructor-init bugprone-dangling-handle + bugprone-fold-init-type + bugprone-forward-declaration-namespace + bugprone-inaccurate-erase bugprone-integer-division bugprone-misplaced-operator-in-strlen-in-alloc + bugprone-move-forwarding-reference + bugprone-multiple-statement-macro bugprone-string-constructor bugprone-suspicious-memset-usage bugprone-undefined-memory-manipulation + bugprone-use-after-move + bugprone-virtual-near-miss cert-dcl03-c (redirects to misc-static-assert) cert-dcl21-cpp cert-dcl50-cpp @@ -82,7 +91,7 @@ hicpp-exception-baseclass hicpp-explicit-conversions (redirects to google-explicit-constructor) hicpp-function-size (redirects to readability-function-size) - hicpp-invalid-access-moved (redirects to misc-use-after-move) + hicpp-invalid-access-moved (redirects to bugprone-use-after-move) hicpp-member-init (redirects to cppcoreguidelines-pro-type-member-init) hicpp-move-const-arg (redirects to misc-move-const-arg) hicpp-named-parameter (redirects to readability-named-parameter) @@ -107,13 +116,8 @@ llvm-include-order llvm-namespace-comment llvm-twine-local - misc-assert-side-effect - misc-bool-pointer-implicit-conversion misc-definitions-in-headers - misc-fold-init-type - misc-forward-declaration-namespace misc-forwarding-reference-overload - misc-inaccurate-erase misc-incorrect-roundings misc-inefficient-algorithm misc-lambda-function-name @@ -123,8 +127,6 @@ misc-misplaced-widening-cast misc-move-const-arg misc-move-constructor-init - misc-move-forwarding-reference - misc-multiple-statement-macro misc-new-delete-overloads misc-noexcept-move-constructor misc-non-copyable-objects @@ -148,8 +150,6 @@ misc-unused-parameters misc-unused-raii misc-unused-using-decls - misc-use-after-move - misc-virtual-near-miss modernize-avoid-bind modernize-deprecated-headers modernize-loop-convert Index: clang-tools-extra/trunk/docs/clang-tidy/checks/misc-assert-side-effect.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/misc-assert-side-effect.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/misc-assert-side-effect.rst @@ -1,23 +0,0 @@ -.. title:: clang-tidy - misc-assert-side-effect - -misc-assert-side-effect -======================= - -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. - -Options -------- - -.. option:: AssertMacros - - A comma-separated list of the names of assert macros to be checked. - -.. option:: 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. Index: clang-tools-extra/trunk/docs/clang-tidy/checks/misc-bool-pointer-implicit-conversion.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/misc-bool-pointer-implicit-conversion.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/misc-bool-pointer-implicit-conversion.rst @@ -1,16 +0,0 @@ -.. title:: clang-tidy - misc-bool-pointer-implicit-conversion - -misc-bool-pointer-implicit-conversion -===================================== - -Checks for conditions based on implicit conversion from a ``bool`` pointer to -``bool``. - -Example: - -.. code-block:: c++ - - bool *p; - if (p) { - // Never used in a pointer-specific way. - } Index: clang-tools-extra/trunk/docs/clang-tidy/checks/misc-fold-init-type.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/misc-fold-init-type.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/misc-fold-init-type.rst @@ -1,27 +0,0 @@ -.. title:: clang-tidy - misc-fold-init-type - -misc-fold-init-type -=================== - -The check flags type mismatches in -`folds `_ -like ``std::accumulate`` that might result in loss of precision. -``std::accumulate`` folds an input range into an initial value using the type of -the latter, with ``operator+`` by default. This can cause loss of precision -through: - -- Truncation: The following code uses a floating point range and an int - initial value, so trucation wil happen at every application of ``operator+`` - and the result will be `0`, which might not be what the user expected. - -.. code-block:: c++ - - auto a = {0.5f, 0.5f, 0.5f, 0.5f}; - return std::accumulate(std::begin(a), std::end(a), 0); - -- Overflow: The following code also returns `0`. - -.. code-block:: c++ - - auto a = {65536LL * 65536 * 65536}; - return std::accumulate(std::begin(a), std::end(a), 0); Index: clang-tools-extra/trunk/docs/clang-tidy/checks/misc-forward-declaration-namespace.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/misc-forward-declaration-namespace.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/misc-forward-declaration-namespace.rst @@ -1,20 +0,0 @@ -.. title:: clang-tidy - misc-forward-declaration-namespace - -misc-forward-declaration-namespace -================================== - -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 existing, which could indicate that -the forward declaration is in a potentially wrong namespace. - -.. code-block:: c++ - - 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::' - -This check can only generate warnings, but it can't suggest a fix at this point. Index: clang-tools-extra/trunk/docs/clang-tidy/checks/misc-inaccurate-erase.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/misc-inaccurate-erase.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/misc-inaccurate-erase.rst @@ -1,13 +0,0 @@ -.. title:: clang-tidy - misc-inaccurate-erase - -misc-inaccurate-erase -===================== - - -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. Index: clang-tools-extra/trunk/docs/clang-tidy/checks/misc-move-forwarding-reference.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/misc-move-forwarding-reference.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/misc-move-forwarding-reference.rst @@ -1,60 +0,0 @@ -.. title:: clang-tidy - misc-move-forwarding-reference - -misc-move-forwarding-reference -============================== - -Warns if ``std::move`` is called on a forwarding reference, for example: - - .. code-block:: c++ - - template - void foo(T&& t) { - bar(std::move(t)); - } - -`Forwarding references -`_ should -typically be passed to ``std::forward`` instead of ``std::move``, and this is -the fix that will be suggested. - -(A forwarding reference is an rvalue reference of a type that is a deduced -function template argument.) - -In this example, the suggested fix would be - - .. code-block:: c++ - - bar(std::forward(t)); - -Background ----------- - -Code like the example above is sometimes written with the expectation that -``T&&`` will always end up being an rvalue reference, no matter what type is -deduced for ``T``, and that it is therefore not possible to pass an lvalue to -``foo()``. However, this is not true. Consider this example: - - .. code-block:: c++ - - std::string s = "Hello, world"; - foo(s); - -This code compiles and, after the call to ``foo()``, ``s`` is left in an -indeterminate state because it has been moved from. This may be surprising to -the caller of ``foo()`` because no ``std::move`` was used when calling -``foo()``. - -The reason for this behavior lies in the special rule for template argument -deduction on function templates like ``foo()`` -- i.e. on function templates -that take an rvalue reference argument of a type that is a deduced function -template argument. (See section [temp.deduct.call]/3 in the C++11 standard.) - -If ``foo()`` is called on an lvalue (as in the example above), then ``T`` is -deduced to be an lvalue reference. In the example, ``T`` is deduced to be -``std::string &``. The type of the argument ``t`` therefore becomes -``std::string& &&``; by the reference collapsing rules, this collapses to -``std::string&``. - -This means that the ``foo(s)`` call passes ``s`` as an lvalue reference, and -``foo()`` ends up moving ``s`` and thereby placing it into an indeterminate -state. Index: clang-tools-extra/trunk/docs/clang-tidy/checks/misc-multiple-statement-macro.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/misc-multiple-statement-macro.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/misc-multiple-statement-macro.rst @@ -1,16 +0,0 @@ -.. title:: clang-tidy - misc-multiple-statement-macro - -misc-multiple-statement-macro -============================= - -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. - -Example: - -.. code-block:: c++ - - #define INCREMENT_TWO(x, y) (x)++; (y)++ - if (do_increment) - INCREMENT_TWO(a, b); // (b)++ will be executed unconditionally. Index: clang-tools-extra/trunk/docs/clang-tidy/checks/misc-use-after-move.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/misc-use-after-move.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/misc-use-after-move.rst @@ -1,203 +0,0 @@ -.. title:: clang-tidy - misc-use-after-move - -misc-use-after-move -=================== - -Warns if an object is used after it has been moved, for example: - - .. code-block:: c++ - - std::string str = "Hello, world!\n"; - std::vector messages; - messages.emplace_back(std::move(str)); - std::cout << str; - -The last line will trigger a warning that ``str`` is used after it has been -moved. - -The check does not trigger a warning if the object is reinitialized after the -move and before the use. For example, no warning will be output for this code: - - .. code-block:: c++ - - messages.emplace_back(std::move(str)); - str = "Greetings, stranger!\n"; - std::cout << str; - -The check takes control flow into account. A warning is only emitted if the use -can be reached from the move. This means that the following code does not -produce a warning: - - .. code-block:: c++ - - if (condition) { - messages.emplace_back(std::move(str)); - } else { - std::cout << str; - } - -On the other hand, the following code does produce a warning: - - .. code-block:: c++ - - for (int i = 0; i < 10; ++i) { - std::cout << str; - messages.emplace_back(std::move(str)); - } - -(The use-after-move happens on the second iteration of the loop.) - -In some cases, the check may not be able to detect that two branches are -mutually exclusive. For example (assuming that ``i`` is an int): - - .. code-block:: c++ - - if (i == 1) { - messages.emplace_back(std::move(str)); - } - if (i == 2) { - std::cout << str; - } - -In this case, the check will erroneously produce a warning, even though it is -not possible for both the move and the use to be executed. - -An erroneous warning can be silenced by reinitializing the object after the -move: - - .. code-block:: c++ - - if (i == 1) { - messages.emplace_back(std::move(str)); - str = ""; - } - if (i == 2) { - std::cout << str; - } - -Subsections below explain more precisely what exactly the check considers to be -a move, use, and reinitialization. - -Unsequenced moves, uses, and reinitializations ----------------------------------------------- - -In many cases, C++ does not make any guarantees about the order in which -sub-expressions of a statement are evaluated. This means that in code like the -following, it is not guaranteed whether the use will happen before or after the -move: - - .. code-block:: c++ - - void f(int i, std::vector v); - std::vector v = { 1, 2, 3 }; - f(v[1], std::move(v)); - -In this kind of situation, the check will note that the use and move are -unsequenced. - -The check will also take sequencing rules into account when reinitializations -occur in the same statement as moves or uses. A reinitialization is only -considered to reinitialize a variable if it is guaranteed to be evaluated after -the move and before the use. - -Move ----- - -The check currently only considers calls of ``std::move`` on local variables or -function parameters. It does not check moves of member variables or global -variables. - -Any call of ``std::move`` on a variable is considered to cause a move of that -variable, even if the result of ``std::move`` is not passed to an rvalue -reference parameter. - -This means that the check will flag a use-after-move even on a type that does -not define a move constructor or move assignment operator. This is intentional. -Developers may use ``std::move`` on such a type in the expectation that the type -will add move semantics in the future. If such a ``std::move`` has the potential -to cause a use-after-move, we want to warn about it even if the type does not -implement move semantics yet. - -Furthermore, if the result of ``std::move`` *is* passed to an rvalue reference -parameter, this will always be considered to cause a move, even if the function -that consumes this parameter does not move from it, or if it does so only -conditionally. For example, in the following situation, the check will assume -that a move always takes place: - - .. code-block:: c++ - - std::vector messages; - void f(std::string &&str) { - // Only remember the message if it isn't empty. - if (!str.empty()) { - messages.emplace_back(std::move(str)); - } - } - std::string str = ""; - f(std::move(str)); - -The check will assume that the last line causes a move, even though, in this -particular case, it does not. Again, this is intentional. - -When analyzing the order in which moves, uses and reinitializations happen (see -section `Unsequenced moves, uses, and reinitializations`_), the move is assumed -to occur in whichever function the result of the ``std::move`` is passed to. - -Use ---- - -Any occurrence of the moved variable that is not a reinitialization (see below) -is considered to be a use. - -An exception to this are objects of type ``std::unique_ptr``, -``std::shared_ptr`` and ``std::weak_ptr``, which have defined move behavior -(objects of these classes are guaranteed to be empty after they have been moved -from). Therefore, an object of these classes will only be considered to be used -if it is dereferenced, i.e. if ``operator*``, ``operator->`` or ``operator[]`` -(in the case of ``std::unique_ptr``) is called on it. - -If multiple uses occur after a move, only the first of these is flagged. - -Reinitialization ----------------- - -The check considers a variable to be reinitialized in the following cases: - - - The variable occurs on the left-hand side of an assignment. - - - The variable is passed to a function as a non-const pointer or non-const - lvalue reference. (It is assumed that the variable may be an out-parameter - for the function.) - - - ``clear()`` or ``assign()`` is called on the variable and the variable is of - one of the standard container types ``basic_string``, ``vector``, ``deque``, - ``forward_list``, ``list``, ``set``, ``map``, ``multiset``, ``multimap``, - ``unordered_set``, ``unordered_map``, ``unordered_multiset``, - ``unordered_multimap``. - - - ``reset()`` is called on the variable and the variable is of type - ``std::unique_ptr``, ``std::shared_ptr`` or ``std::weak_ptr``. - -If the variable in question is a struct and an individual member variable of -that struct is written to, the check does not consider this to be a -reinitialization -- even if, eventually, all member variables of the struct are -written to. For example: - - .. code-block:: c++ - - struct S { - std::string str; - int i; - }; - S s = { "Hello, world!\n", 42 }; - S s_other = std::move(s); - s.str = "Lorem ipsum"; - s.i = 99; - -The check will not consider ``s`` to be reinitialized after the last line; -instead, the line that assigns to ``s.str`` will be flagged as a use-after-move. -This is intentional as this pattern of reinitializing a struct is error-prone. -For example, if an additional member variable is added to ``S``, it is easy to -forget to add the reinitialization for this additional member. Instead, it is -safer to assign to the entire struct in one go, and this will also avoid the -use-after-move warning. Index: clang-tools-extra/trunk/docs/clang-tidy/checks/misc-virtual-near-miss.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/misc-virtual-near-miss.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/misc-virtual-near-miss.rst @@ -1,20 +0,0 @@ -.. title:: clang-tidy - misc-virtual-near-miss - -misc-virtual-near-miss -====================== - -Warn if a function is a near miss (ie. the name is very similar and the function -signiture is the same) to a virtual function from a base class. - -Example: - -.. code-block:: c++ - - struct Base { - virtual void func(); - }; - - struct Derived : Base { - virtual funk(); - // warning: 'Derived::funk' has a similar name and the same signature as virtual method 'Base::func'; did you mean to override it? - }; Index: clang-tools-extra/trunk/test/clang-tidy/bugprone-assert-side-effect.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/bugprone-assert-side-effect.cpp +++ clang-tools-extra/trunk/test/clang-tidy/bugprone-assert-side-effect.cpp @@ -0,0 +1,114 @@ +// RUN: %check_clang_tidy %s bugprone-assert-side-effect %t -- -config="{CheckOptions: [{key: bugprone-assert-side-effect.CheckFunctionCalls, value: 1}, {key: bugprone-assert-side-effect.AssertMacros, value: 'assert,assert2,my_assert,convoluted_assert,msvc_assert'}]}" -- -fexceptions + +//===--- assert definition block ------------------------------------------===// +int abort() { return 0; } + +#ifdef NDEBUG +#define assert(x) 1 +#else +#define assert(x) \ + if (!(x)) \ + (void)abort() +#endif + +void print(...); +#define assert2(e) (__builtin_expect(!(e), 0) ? \ + print (#e, __FILE__, __LINE__) : (void)0) + +#ifdef NDEBUG +#define my_assert(x) 1 +#else +#define my_assert(x) \ + ((void)((x) ? 1 : abort())) +#endif + +#ifdef NDEBUG +#define not_my_assert(x) 1 +#else +#define not_my_assert(x) \ + if (!(x)) \ + (void)abort() +#endif + +#define real_assert(x) ((void)((x) ? 1 : abort())) +#define wrap1(x) real_assert(x) +#define wrap2(x) wrap1(x) +#define convoluted_assert(x) wrap2(x) + +#define msvc_assert(expression) (void)( \ + (!!(expression)) || \ + (abort(), 0) \ + ) + + +//===----------------------------------------------------------------------===// + +class MyClass { +public: + bool badFunc(int a, int b) { return a * b > 0; } + bool goodFunc(int a, int b) const { return a * b > 0; } + + MyClass &operator=(const MyClass &rhs) { return *this; } + + int operator-() { return 1; } + + operator bool() const { return true; } + + void operator delete(void *p) {} +}; + +bool freeFunction() { + return true; +} + +int main() { + + int X = 0; + bool B = false; + assert(X == 1); + + assert(X = 1); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect [bugprone-assert-side-effect] + my_assert(X = 1); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found my_assert() with side effect + convoluted_assert(X = 1); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found convoluted_assert() with side effect + not_my_assert(X = 1); + + assert(++X); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect + assert(!B); + + assert(B || true); + + assert(freeFunction()); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect + + MyClass mc; + assert(mc.badFunc(0, 1)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect + assert(mc.goodFunc(0, 1)); + + MyClass mc2; + assert(mc2 = mc); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect + + assert(-mc > 0); + + MyClass *mcp; + assert(mcp = new MyClass); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect + + assert((delete mcp, false)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect + + assert((throw 1, false)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect + + assert2(1 == 2 - 1); + + msvc_assert(mc2 = mc); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found msvc_assert() with side effect + + return 0; +} Index: clang-tools-extra/trunk/test/clang-tidy/bugprone-bool-pointer-implicit-conversion.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/bugprone-bool-pointer-implicit-conversion.cpp +++ clang-tools-extra/trunk/test/clang-tidy/bugprone-bool-pointer-implicit-conversion.cpp @@ -0,0 +1,82 @@ +// RUN: %check_clang_tidy %s bugprone-bool-pointer-implicit-conversion %t + +bool *SomeFunction(); +void SomeOtherFunction(bool*); +bool F(); +void G(bool); + + +template +void t(T b) { + if (b) { + } +} + +void foo() { + bool *b = SomeFunction(); + if (b) { +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: dubious check of 'bool *' against 'nullptr' +// CHECK-FIXES: if (*b) { + } + + if (F() && b) { +// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: dubious check of 'bool *' against 'nullptr' +// CHECK-FIXES: if (F() && *b) { + } + + // TODO: warn here. + if (b) { + G(b); + } + +#define TESTMACRO if (b || F()) + + TESTMACRO { + } + + t(b); + + if (!b) { + // no-warning + } + + if (SomeFunction()) { + // no-warning + } + + bool *c = SomeFunction(); + if (c) { + (void)c; + (void)*c; // no-warning + } + + if (c) { + *c = true; // no-warning + } + + if (c) { + c[0] = false; // no-warning + } + + if (c) { + SomeOtherFunction(c); // no-warning + } + + if (c) { + delete[] c; // no-warning + } + + if (c) { + *(c) = false; // no-warning + } + + struct { + bool *b; + } d = { SomeFunction() }; + + if (d.b) + (void)*d.b; // no-warning + +#define CHECK(b) if (b) {} + CHECK(c) +} Index: clang-tools-extra/trunk/test/clang-tidy/bugprone-fold-init-type.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/bugprone-fold-init-type.cpp +++ clang-tools-extra/trunk/test/clang-tidy/bugprone-fold-init-type.cpp @@ -0,0 +1,158 @@ +// RUN: %check_clang_tidy %s bugprone-fold-init-type %t + +namespace std { +template +T accumulate(InputIt first, InputIt last, T init); + +template +T reduce(InputIt first, InputIt last, T init); +template +T reduce(ExecutionPolicy &&policy, + InputIt first, InputIt last, T init); + +struct parallel_execution_policy {}; +constexpr parallel_execution_policy par{}; + +template +T inner_product(InputIt1 first1, InputIt1 last1, + InputIt2 first2, T value); + +template +T inner_product(ExecutionPolicy &&policy, InputIt1 first1, InputIt1 last1, + InputIt2 first2, T value); + +} // namespace std + +struct FloatIterator { + typedef float value_type; +}; +template +struct TypedefTemplateIterator { typedef ValueType value_type; }; +template +struct UsingTemplateIterator { using value_type = ValueType; }; +template +struct DependentTypedefTemplateIterator { typedef typename ValueType::value_type value_type; }; +template +struct DependentUsingTemplateIterator : public TypedefTemplateIterator { using typename TypedefTemplateIterator::value_type; }; +using TypedeffedIterator = FloatIterator; + +// Positives. + +int accumulatePositive1() { + float a[1] = {0.5f}; + return std::accumulate(a, a + 1, 0); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int' +} + +int accumulatePositive2() { + FloatIterator it; + return std::accumulate(it, it, 0); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int' +} + +int accumulatePositive3() { + double a[1] = {0.0}; + return std::accumulate(a, a + 1, 0.0f); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'double' into type 'float' +} + +int accumulatePositive4() { + TypedefTemplateIterator it; + return std::accumulate(it, it, 0); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int' +} + +int accumulatePositive5() { + UsingTemplateIterator it; + return std::accumulate(it, it, 0); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int' +} + +int accumulatePositive6() { + DependentTypedefTemplateIterator> it; + return std::accumulate(it, it, 0); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int' +} + +int accumulatePositive7() { + TypedeffedIterator it; + return std::accumulate(it, it, 0); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int' +} + +int accumulatePositive8() { + DependentUsingTemplateIterator it; + return std::accumulate(it, it, 0); + // FIXME: this one should trigger too. +} + +int reducePositive1() { + float a[1] = {0.5f}; + return std::reduce(a, a + 1, 0); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int' +} + +int reducePositive2() { + float a[1] = {0.5f}; + return std::reduce(std::par, a, a + 1, 0); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int' +} + +int innerProductPositive1() { + float a[1] = {0.5f}; + int b[1] = {1}; + return std::inner_product(std::par, a, a + 1, b, 0); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int' +} + +int innerProductPositive2() { + float a[1] = {0.5f}; + int b[1] = {1}; + return std::inner_product(std::par, a, a + 1, b, 0); + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int' +} + +// Negatives. + +int negative1() { + float a[1] = {0.5f}; + // This is OK because types match. + return std::accumulate(a, a + 1, 0.0); +} + +int negative2() { + float a[1] = {0.5f}; + // This is OK because double is bigger than float. + return std::accumulate(a, a + 1, 0.0); +} + +int negative3() { + float a[1] = {0.5f}; + // This is OK because the user explicitly specified T. + return std::accumulate(a, a + 1, 0); +} + +int negative4() { + TypedefTemplateIterator it; + // For now this is OK. + return std::accumulate(it, it, 0.0); +} + +int negative5() { + float a[1] = {0.5f}; + float b[1] = {1.0f}; + return std::inner_product(std::par, a, a + 1, b, 0.0f); +} + +namespace blah { +namespace std { +template +T accumulate(InputIt, InputIt, T); // We should not care about this one. +} + +int negative5() { + float a[1] = {0.5f}; + // Note that this is using blah::std::accumulate. + return std::accumulate(a, a + 1, 0); +} +} Index: clang-tools-extra/trunk/test/clang-tidy/bugprone-forward-declaration-namespace.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/bugprone-forward-declaration-namespace.cpp +++ clang-tools-extra/trunk/test/clang-tidy/bugprone-forward-declaration-namespace.cpp @@ -0,0 +1,163 @@ +// RUN: %check_clang_tidy %s bugprone-forward-declaration-namespace %t + +namespace { +// This is a declaration in a wrong namespace. +class T_A; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'T_A' is never referenced, but a declaration with the same name found in another namespace 'na' [bugprone-forward-declaration-namespace] +// CHECK-MESSAGES: note: a declaration of 'T_A' is found here +// CHECK-MESSAGES: :[[@LINE-3]]:7: warning: no definition found for 'T_A', but a definition with the same name 'T_A' found in another namespace '(global)' [bugprone-forward-declaration-namespace] +// CHECK-MESSAGES: note: a definition of 'T_A' is found here +} + +namespace na { +// This is a declaration in a wrong namespace. +class T_A; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'T_A' is never referenced, but a declaration with the same name found in another namespace '(anonymous)' +// CHECK-MESSAGES: note: a declaration of 'T_A' is found here +// CHECK-MESSAGES: :[[@LINE-3]]:7: warning: no definition found for 'T_A', but a definition with the same name 'T_A' found in another namespace '(global)' +// CHECK-MESSAGES: note: a definition of 'T_A' is found here +} + +class T_A; + +class T_A { + int x; +}; + +class NESTED; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: no definition found for 'NESTED', but a definition with the same name 'NESTED' found in another namespace '(anonymous namespace)::nq::(anonymous)' +// CHECK-MESSAGES: note: a definition of 'NESTED' is found here + +namespace { +namespace nq { +namespace { +class NESTED {}; +} +} +} + +namespace na { +class T_B; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'T_B' is never referenced, but a declaration with the same name found in another namespace 'nb' +// CHECK-MESSAGES: note: a declaration of 'T_B' is found here +// CHECK-MESSAGES: :[[@LINE-3]]:7: warning: no definition found for 'T_B', but a definition with the same name 'T_B' found in another namespace 'nb' +// CHECK-MESSAGES: note: a definition of 'T_B' is found here +} + +namespace nb { +class T_B; +} + +namespace nb { +class T_B { + int x; +}; +} + +namespace na { +class T_B; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'T_B' is never referenced, but a declaration with the same name found in another namespace 'nb' +// CHECK-MESSAGES: note: a declaration of 'T_B' is found here +// CHECK-MESSAGES: :[[@LINE-3]]:7: warning: no definition found for 'T_B', but a definition with the same name 'T_B' found in another namespace 'nb' +// CHECK-MESSAGES: note: a definition of 'T_B' is found here +} + +// A simple forward declaration. Although it is never used, but no declaration +// with the same name is found in other namespace. +class OUTSIDER; + +namespace na { +// This class is referenced declaration, we don't generate warning. +class OUTSIDER_1; +} + +void f(na::OUTSIDER_1); + +namespace nc { +// This class is referenced as friend in OOP. +class OUTSIDER_1; + +class OOP { + friend struct OUTSIDER_1; +}; +} + +namespace nd { +class OUTSIDER_1; +void f(OUTSIDER_1 *); +} + +namespace nb { +class OUTSIDER_1; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'OUTSIDER_1' is never referenced, but a declaration with the same name found in another namespace 'na' +// CHECK-MESSAGES: note: a declaration of 'OUTSIDER_1' is found here +} + + +namespace na { +template +class T_C; +} + +namespace nb { +// FIXME: this is an error, but we don't consider template class declaration +// now. +template +class T_C; +} + +namespace na { +template +class T_C { + int x; +}; +} + +namespace na { + +template +class T_TEMP { + template + struct rebind { typedef T_TEMP<_Tp1> other; }; +}; + +// We ignore class template specialization. +template class T_TEMP; +} + +namespace nb { + +template +class T_TEMP_1 { + template + struct rebind { typedef T_TEMP_1<_Tp1> other; }; +}; + +// We ignore class template specialization. +extern template class T_TEMP_1; +} + +namespace nd { +class D; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'D' is never referenced, but a declaration with the same name found in another namespace 'nd::ne' +// CHECK-MESSAGES: note: a declaration of 'D' is found here +} + +namespace nd { +namespace ne { +class D; +} +} + +int f(nd::ne::D &d); + + +// This should be ignored by the check. +template +class Observer { + class Impl; +}; + +template +class Observer::Impl { +}; Index: clang-tools-extra/trunk/test/clang-tidy/bugprone-inaccurate-erase.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/bugprone-inaccurate-erase.cpp +++ clang-tools-extra/trunk/test/clang-tidy/bugprone-inaccurate-erase.cpp @@ -0,0 +1,100 @@ +// RUN: %check_clang_tidy %s bugprone-inaccurate-erase %t + +namespace std { +template struct vec_iterator { + T ptr; + vec_iterator operator++(int); + + template + vec_iterator(const vec_iterator &); // Omit enable_if<...>. +}; + +template struct vector { + typedef vec_iterator iterator; + + iterator begin(); + iterator end(); + + void erase(iterator); + void erase(iterator, iterator); +}; + +template struct vector_with_const_iterator { + typedef vec_iterator iterator; + typedef vec_iterator const_iterator; + + iterator begin(); + iterator end(); + + void erase(const_iterator); + void erase(const_iterator, const_iterator); +}; + +template +FwIt remove(FwIt begin, FwIt end, const T &val); + +template +FwIt remove_if(FwIt begin, FwIt end, Func f); + +template FwIt unique(FwIt begin, FwIt end); + +template struct unique_ptr {}; +} // namespace std + +struct custom_iter {}; +struct custom_container { + void erase(...); + custom_iter begin(); + custom_iter end(); +}; + +template void g() { + T t; + t.erase(std::remove(t.begin(), t.end(), 10)); + // CHECK-FIXES: {{^ }}t.erase(std::remove(t.begin(), t.end(), 10));{{$}} + + std::vector v; + v.erase(remove(v.begin(), v.end(), 10)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this call will remove at most one + // CHECK-FIXES: {{^ }}v.erase(remove(v.begin(), v.end(), 10), v.end());{{$}} +} + +#define ERASE(x, y) x.erase(remove(x.begin(), x.end(), y)) +// CHECK-FIXES: #define ERASE(x, y) x.erase(remove(x.begin(), x.end(), y)) + +int main() { + std::vector v; + + v.erase(remove(v.begin(), v.end(), 10)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this call will remove at most one item even when multiple items should be removed [bugprone-inaccurate-erase] + // CHECK-FIXES: {{^ }}v.erase(remove(v.begin(), v.end(), 10), v.end());{{$}} + v.erase(remove(v.begin(), v.end(), 20), v.end()); + + auto *p = &v; + p->erase(remove(p->begin(), p->end(), 11)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this call will remove at most one + // CHECK-FIXES: {{^ }}p->erase(remove(p->begin(), p->end(), 11), p->end());{{$}} + + std::vector_with_const_iterator v2; + v2.erase(remove(v2.begin(), v2.end(), 12)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this call will remove at most one + // CHECK-FIXES: {{^ }}v2.erase(remove(v2.begin(), v2.end(), 12), v2.end());{{$}} + + // Fix is not trivial. + auto it = v.end(); + v.erase(remove(v.begin(), it, 10)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this call will remove at most one + // CHECK-FIXES: {{^ }}v.erase(remove(v.begin(), it, 10));{{$}} + + g>(); + g(); + + ERASE(v, 15); + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: this call will remove at most one + // CHECK-FIXES: {{^ }}ERASE(v, 15);{{$}} + + std::vector> vupi; + auto iter = vupi.begin(); + vupi.erase(iter++); + // CHECK-FIXES: {{^ }}vupi.erase(iter++);{{$}} +} Index: clang-tools-extra/trunk/test/clang-tidy/bugprone-move-forwarding-reference.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/bugprone-move-forwarding-reference.cpp +++ clang-tools-extra/trunk/test/clang-tidy/bugprone-move-forwarding-reference.cpp @@ -0,0 +1,125 @@ +// RUN: %check_clang_tidy %s bugprone-move-forwarding-reference %t -- -- -std=c++14 -fno-delayed-template-parsing + +namespace std { +template struct remove_reference; + +template struct remove_reference { typedef _Tp type; }; + +template struct remove_reference<_Tp &> { typedef _Tp type; }; + +template struct remove_reference<_Tp &&> { typedef _Tp type; }; + +template +constexpr typename std::remove_reference<_Tp>::type &&move(_Tp &&__t); + +} // namespace std + +// Standard case. +template void f1(U &&SomeU) { + T SomeT(std::move(SomeU)); + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to + // CHECK-FIXES: T SomeT(std::forward(SomeU)); +} + +// Ignore parentheses around the argument to std::move(). +template void f2(U &&SomeU) { + T SomeT(std::move((SomeU))); + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to + // CHECK-FIXES: T SomeT(std::forward((SomeU))); +} + +// Handle the case correctly where std::move() is being used through a using +// declaration. +template void f3(U &&SomeU) { + using std::move; + T SomeT(move(SomeU)); + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to + // CHECK-FIXES: T SomeT(std::forward(SomeU)); +} + +// Handle the case correctly where a global specifier is prepended to +// std::move(). +template void f4(U &&SomeU) { + T SomeT(::std::move(SomeU)); + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to + // CHECK-FIXES: T SomeT(::std::forward(SomeU)); +} + +// Create a correct fix if there are spaces around the scope resolution +// operator. +template void f5(U &&SomeU) { + { + T SomeT(:: std :: move(SomeU)); + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: forwarding reference passed to + // CHECK-FIXES: T SomeT(::std::forward(SomeU)); + } + { + T SomeT(std :: move(SomeU)); + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: forwarding reference passed to + // CHECK-FIXES: T SomeT(std::forward(SomeU)); + } +} + +// Ignore const rvalue reference parameters. +template void f6(const U &&SomeU) { + T SomeT(std::move(SomeU)); +} + +// Ignore the case where the argument to std::move() is a lambda parameter (and +// thus not actually a parameter of the function template). +template void f7() { + [](U &&SomeU) { T SomeT(std::move(SomeU)); }; +} + +// Ignore the case where the argument is a lvalue reference. +template void f8(U &SomeU) { + T SomeT(std::move(SomeU)); +} + +// Ignore the case where the template parameter is a class template parameter +// (i.e. no template argument deduction is taking place). +template class SomeClass { + void f(U &&SomeU) { T SomeT(std::move(SomeU)); } +}; + +// Ignore the case where the function parameter in the template isn't an rvalue +// reference but the template argument is explicitly set to be an rvalue +// reference. +class A {}; +template void foo(T); +void f8() { + A a; + foo(std::move(a)); +} + +// A warning is output, but no fix is suggested, if a macro is used to rename +// std::move. +#define MOVE(x) std::move((x)) +template void f9(U &&SomeU) { + T SomeT(MOVE(SomeU)); + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to +} + +// Same result if the argument is passed outside of the macro. +#undef MOVE +#define MOVE std::move +template void f10(U &&SomeU) { + T SomeT(MOVE(SomeU)); + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to +} + +// Same result if the macro does not include the "std" namespace. +#undef MOVE +#define MOVE move +template void f11(U &&SomeU) { + T SomeT(std::MOVE(SomeU)); + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to +} + +// Handle the case correctly where the forwarding reference is a parameter of a +// generic lambda. +template void f12() { + [] (auto&& x) { T SomeT(std::move(x)); }; + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: forwarding reference passed to + // CHECK-FIXES: [] (auto&& x) { T SomeT(std::forward(x)); } +} Index: clang-tools-extra/trunk/test/clang-tidy/bugprone-multiple-statement-macro.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/bugprone-multiple-statement-macro.cpp +++ clang-tools-extra/trunk/test/clang-tidy/bugprone-multiple-statement-macro.cpp @@ -0,0 +1,85 @@ +// RUN: %check_clang_tidy %s bugprone-multiple-statement-macro %t + +void F(); + +#define BAD_MACRO(x) \ + F(); \ + F() + +#define GOOD_MACRO(x) \ + do { \ + F(); \ + F(); \ + } while (0) + +#define GOOD_MACRO2(x) F() + +#define GOOD_MACRO3(x) F(); + +#define MACRO_ARG_MACRO(X) \ + if (54) \ + X(2) + +#define ALL_IN_MACRO(X) \ + if (43) \ + F(); \ + F() + +#define GOOD_NESTED(x) \ + if (x) \ + GOOD_MACRO3(x); \ + F(); + +#define IF(x) if(x) + +void positives() { + if (1) + BAD_MACRO(1); + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple statement macro used without braces; some statements will be unconditionally executed [bugprone-multiple-statement-macro] + if (1) { + } else + BAD_MACRO(1); + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple statement macro used + while (1) + BAD_MACRO(1); + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple statement macro used + for (;;) + BAD_MACRO(1); + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple statement macro used + + MACRO_ARG_MACRO(BAD_MACRO); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: multiple statement macro used + MACRO_ARG_MACRO(F(); int); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: multiple statement macro used + IF(1) BAD_MACRO(1); + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: multiple statement macro used +} + +void negatives() { + if (1) { + BAD_MACRO(1); + } else { + BAD_MACRO(1); + } + while (1) { + BAD_MACRO(1); + } + for (;;) { + BAD_MACRO(1); + } + + if (1) + GOOD_MACRO(1); + if (1) { + GOOD_MACRO(1); + } + if (1) + GOOD_MACRO2(1); + if (1) + GOOD_MACRO3(1); + + MACRO_ARG_MACRO(GOOD_MACRO); + ALL_IN_MACRO(1); + + IF(1) GOOD_MACRO(1); +} Index: clang-tools-extra/trunk/test/clang-tidy/bugprone-use-after-move.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/bugprone-use-after-move.cpp +++ clang-tools-extra/trunk/test/clang-tidy/bugprone-use-after-move.cpp @@ -0,0 +1,1164 @@ +// RUN: %check_clang_tidy %s bugprone-use-after-move %t -- -- -std=c++11 -fno-delayed-template-parsing + +typedef decltype(nullptr) nullptr_t; + +namespace std { +typedef unsigned size_t; + +template +struct unique_ptr { + unique_ptr(); + T *get() const; + explicit operator bool() const; + void reset(T *ptr); + T &operator*() const; + T *operator->() const; + T& operator[](size_t i) const; +}; + +template +struct shared_ptr { + shared_ptr(); + T *get() const; + explicit operator bool() const; + void reset(T *ptr); + T &operator*() const; + T *operator->() const; +}; + +template +struct weak_ptr { + weak_ptr(); + bool expired() const; +}; + +#define DECLARE_STANDARD_CONTAINER(name) \ + template \ + struct name { \ + name(); \ + void clear(); \ + bool empty(); \ + } + +#define DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(name) \ + template \ + struct name { \ + name(); \ + void clear(); \ + bool empty(); \ + void assign(size_t, const T &); \ + } + +DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(basic_string); +DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(vector); +DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(deque); +DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(forward_list); +DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(list); +DECLARE_STANDARD_CONTAINER(set); +DECLARE_STANDARD_CONTAINER(map); +DECLARE_STANDARD_CONTAINER(multiset); +DECLARE_STANDARD_CONTAINER(multimap); +DECLARE_STANDARD_CONTAINER(unordered_set); +DECLARE_STANDARD_CONTAINER(unordered_map); +DECLARE_STANDARD_CONTAINER(unordered_multiset); +DECLARE_STANDARD_CONTAINER(unordered_multimap); + +typedef basic_string string; + +template +struct remove_reference; + +template +struct remove_reference { + typedef _Tp type; +}; + +template +struct remove_reference<_Tp &> { + typedef _Tp type; +}; + +template +struct remove_reference<_Tp &&> { + typedef _Tp type; +}; + +template +constexpr typename std::remove_reference<_Tp>::type &&move(_Tp &&__t) noexcept { + return static_cast::type &&>(__t); +} + +} // namespace std + +class A { +public: + A(); + A(const A &); + A(A &&); + + A &operator=(const A &); + A &operator=(A &&); + + void foo() const; + int getInt() const; + + operator bool() const; + + int i; +}; + +//////////////////////////////////////////////////////////////////////////////// +// General tests. + +// Simple case. +void simple() { + A a; + a.foo(); + A other_a = std::move(a); + a.foo(); + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:15: note: move occurred here +} + +// A warning should only be emitted for one use-after-move. +void onlyFlagOneUseAfterMove() { + A a; + a.foo(); + A other_a = std::move(a); + a.foo(); + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:15: note: move occurred here + a.foo(); +} + +void moveAfterMove() { + // Move-after-move also counts as a use. + { + A a; + std::move(a); + std::move(a); + // CHECK-MESSAGES: [[@LINE-1]]:15: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + } + // This is also true if the move itself turns into the use on the second loop + // iteration. + { + A a; + for (int i = 0; i < 10; ++i) { + std::move(a); + // CHECK-MESSAGES: [[@LINE-1]]:17: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-2]]:7: note: move occurred here + // CHECK-MESSAGES: [[@LINE-3]]:17: note: the use happens in a later loop + } + } +} + +// Checks also works on function parameters that have a use-after move. +void parameters(A a) { + std::move(a); + a.foo(); + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:3: note: move occurred here +} + +void standardSmartPtr() { + // std::unique_ptr<>, std::shared_ptr<> and std::weak_ptr<> are guaranteed to + // be null after a std::move. So the check only flags accesses that would + // dereference the pointer. + { + std::unique_ptr ptr; + std::move(ptr); + ptr.get(); + static_cast(ptr); + *ptr; + // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'ptr' used after it was moved + // CHECK-MESSAGES: [[@LINE-5]]:5: note: move occurred here + } + { + std::unique_ptr ptr; + std::move(ptr); + ptr->foo(); + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + } + { + std::unique_ptr ptr; + std::move(ptr); + ptr[0]; + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + } + { + std::shared_ptr ptr; + std::move(ptr); + ptr.get(); + static_cast(ptr); + *ptr; + // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'ptr' used after it was moved + // CHECK-MESSAGES: [[@LINE-5]]:5: note: move occurred here + } + { + std::shared_ptr ptr; + std::move(ptr); + ptr->foo(); + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + } + { + // std::weak_ptr<> cannot be dereferenced directly, so we only check that + // member functions may be called on it after a move. + std::weak_ptr ptr; + std::move(ptr); + ptr.expired(); + } + // Make sure we recognize std::unique_ptr<> or std::shared_ptr<> if they're + // wrapped in a typedef. + { + typedef std::unique_ptr PtrToA; + PtrToA ptr; + std::move(ptr); + ptr.get(); + } + { + typedef std::shared_ptr PtrToA; + PtrToA ptr; + std::move(ptr); + ptr.get(); + } + // And we don't get confused if the template argument is a little more + // involved. + { + struct B { + typedef A AnotherNameForA; + }; + std::unique_ptr ptr; + std::move(ptr); + ptr.get(); + } + // We don't give any special treatment to types that are called "unique_ptr" + // or "shared_ptr" but are not in the "::std" namespace. + { + struct unique_ptr { + void get(); + } ptr; + std::move(ptr); + ptr.get(); + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + } +} + +// The check also works in member functions. +class Container { + void useAfterMoveInMemberFunction() { + A a; + std::move(a); + a.foo(); + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + } +}; + +// We see the std::move() if it's inside a declaration. +void moveInDeclaration() { + A a; + A another_a(std::move(a)); + a.foo(); + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here +} + +// We see the std::move if it's inside an initializer list. Initializer lists +// are a special case because they cause ASTContext::getParents() to return +// multiple parents for certain nodes in their subtree. This is because +// RecursiveASTVisitor visits both the syntactic and semantic forms of +// InitListExpr, and the parent-child relationships are different between the +// two forms. +void moveInInitList() { + struct S { + A a; + }; + A a; + S s{std::move(a)}; + a.foo(); + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:7: note: move occurred here +} + +void lambdas() { + // Use-after-moves inside a lambda should be detected. + { + A a; + auto lambda = [a] { + std::move(a); + a.foo(); + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:7: note: move occurred here + }; + } + // This is just as true if the variable was declared inside the lambda. + { + auto lambda = [] { + A a; + std::move(a); + a.foo(); + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:7: note: move occurred here + }; + } + // But don't warn if the move happened inside the lambda but the use happened + // outside -- because + // - the 'a' inside the lambda is a copy, and + // - we don't know when the lambda will get called anyway + { + A a; + auto lambda = [a] { + std::move(a); + }; + a.foo(); + } + // Warn if the use consists of a capture that happens after a move. + { + A a; + std::move(a); + auto lambda = [a]() { a.foo(); }; + // CHECK-MESSAGES: [[@LINE-1]]:20: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + } + // ...even if the capture was implicit. + { + A a; + std::move(a); + auto lambda = [=]() { a.foo(); }; + // CHECK-MESSAGES: [[@LINE-1]]:27: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + } + // Same tests but for capture by reference. + { + A a; + std::move(a); + auto lambda = [&a]() { a.foo(); }; + // CHECK-MESSAGES: [[@LINE-1]]:21: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + } + { + A a; + std::move(a); + auto lambda = [&]() { a.foo(); }; + // CHECK-MESSAGES: [[@LINE-1]]:27: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + } + // But don't warn if the move happened after the capture. + { + A a; + auto lambda = [a]() { a.foo(); }; + std::move(a); + } + // ...and again, same thing with an implicit move. + { + A a; + auto lambda = [=]() { a.foo(); }; + std::move(a); + } + // Same tests but for capture by reference. + { + A a; + auto lambda = [&a]() { a.foo(); }; + std::move(a); + } + { + A a; + auto lambda = [&]() { a.foo(); }; + std::move(a); + } +} + +// Use-after-moves are detected in uninstantiated templates if the moved type +// is not a dependent type. +template +void movedTypeIsNotDependentType() { + T t; + A a; + std::move(a); + a.foo(); + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:3: note: move occurred here +} + +// And if the moved type is a dependent type, the use-after-move is detected if +// the template is instantiated. +template +void movedTypeIsDependentType() { + T t; + std::move(t); + t.foo(); + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 't' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:3: note: move occurred here +} +template void movedTypeIsDependentType(); + +// We handle the case correctly where the move consists of an implicit call +// to a conversion operator. +void implicitConversionOperator() { + struct Convertible { + operator A() && { return A(); } + }; + void takeA(A a); + + Convertible convertible; + takeA(std::move(convertible)); + convertible; + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'convertible' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:9: note: move occurred here +} + +// Using decltype on an expression is not a use. +void decltypeIsNotUse() { + A a; + std::move(a); + decltype(a) other_a; +} + +// Ignore moves or uses that occur as part of template arguments. +template +class ClassTemplate { +public: + void foo(A a); +}; +template +void functionTemplate(A a); +void templateArgIsNotUse() { + { + // A pattern like this occurs in the EXPECT_EQ and ASSERT_EQ macros in + // Google Test. + A a; + ClassTemplate().foo(std::move(a)); + } + { + A a; + functionTemplate(std::move(a)); + } +} + +// Ignore moves of global variables. +A global_a; +void ignoreGlobalVariables() { + std::move(global_a); + global_a.foo(); +} + +// Ignore moves of member variables. +class IgnoreMemberVariables { + A a; + static A static_a; + + void f() { + std::move(a); + a.foo(); + + std::move(static_a); + static_a.foo(); + } +}; + +//////////////////////////////////////////////////////////////////////////////// +// Tests involving control flow. + +void useAndMoveInLoop() { + // Warn about use-after-moves if they happen in a later loop iteration than + // the std::move(). + { + A a; + for (int i = 0; i < 10; ++i) { + a.foo(); + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE+2]]:7: note: move occurred here + // CHECK-MESSAGES: [[@LINE-3]]:7: note: the use happens in a later loop + std::move(a); + } + } + // However, this case shouldn't be flagged -- the scope of the declaration of + // 'a' is important. + { + for (int i = 0; i < 10; ++i) { + A a; + a.foo(); + std::move(a); + } + } + // Same as above, except that we have an unrelated variable being declared in + // the same declaration as 'a'. This case is interesting because it tests that + // the synthetic DeclStmts generated by the CFG are sequenced correctly + // relative to the other statements. + { + for (int i = 0; i < 10; ++i) { + A a, other; + a.foo(); + std::move(a); + } + } + // Don't warn if we return after the move. + { + A a; + for (int i = 0; i < 10; ++i) { + a.foo(); + if (a.getInt() > 0) { + std::move(a); + return; + } + } + } +} + +void differentBranches(int i) { + // Don't warn if the use is in a different branch from the move. + { + A a; + if (i > 0) { + std::move(a); + } else { + a.foo(); + } + } + // Same thing, but with a ternary operator. + { + A a; + i > 0 ? (void)std::move(a) : a.foo(); + } + // A variation on the theme above. + { + A a; + a.getInt() > 0 ? a.getInt() : A(std::move(a)).getInt(); + } + // Same thing, but with a switch statement. + { + A a; + switch (i) { + case 1: + std::move(a); + break; + case 2: + a.foo(); + break; + } + } + // However, if there's a fallthrough, we do warn. + { + A a; + switch (i) { + case 1: + std::move(a); + case 2: + a.foo(); + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-4]]:7: note: move occurred here + break; + } + } +} + +// False positive: A use-after-move is flagged even though the "if (b)" and +// "if (!b)" are mutually exclusive. +void mutuallyExclusiveBranchesFalsePositive(bool b) { + A a; + if (b) { + std::move(a); + } + if (!b) { + a.foo(); + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-5]]:5: note: move occurred here + } +} + +// Destructors marked [[noreturn]] are handled correctly in the control flow +// analysis. (These are used in some styles of assertion macros.) +class FailureLogger { +public: + FailureLogger(); + [[noreturn]] ~FailureLogger(); + void log(const char *); +}; +#define ASSERT(x) \ + while (x) \ + FailureLogger().log(#x) +bool operationOnA(A); +void noreturnDestructor() { + A a; + // The while loop in the ASSERT() would ordinarily have the potential to cause + // a use-after-move because the second iteration of the loop would be using a + // variable that had been moved from in the first iteration. Check that the + // CFG knows that the second iteration of the loop is never reached because + // the FailureLogger destructor is marked [[noreturn]]. + ASSERT(operationOnA(std::move(a))); +} +#undef ASSERT + +//////////////////////////////////////////////////////////////////////////////// +// Tests for reinitializations + +template +void swap(T &a, T &b) { + T tmp = std::move(a); + a = std::move(b); + b = std::move(tmp); +} +void assignments(int i) { + // Don't report a use-after-move if the variable was assigned to in the + // meantime. + { + A a; + std::move(a); + a = A(); + a.foo(); + } + // The assignment should also be recognized if move, assignment and use don't + // all happen in the same block (but the assignment is still guaranteed to + // prevent a use-after-move). + { + A a; + if (i == 1) { + std::move(a); + a = A(); + } + if (i == 2) { + a.foo(); + } + } + { + A a; + if (i == 1) { + std::move(a); + } + if (i == 2) { + a = A(); + a.foo(); + } + } + // The built-in assignment operator should also be recognized as a + // reinitialization. (std::move() may be called on built-in types in template + // code.) + { + int a1 = 1, a2 = 2; + swap(a1, a2); + } + // A std::move() after the assignment makes the variable invalid again. + { + A a; + std::move(a); + a = A(); + std::move(a); + a.foo(); + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + } + // Report a use-after-move if we can't be sure that the variable was assigned + // to. + { + A a; + std::move(a); + if (i < 10) { + a = A(); + } + if (i > 5) { + a.foo(); + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-7]]:5: note: move occurred here + } + } +} + +// Passing the object to a function through a non-const pointer or reference +// counts as a re-initialization. +void passByNonConstPointer(A *); +void passByNonConstReference(A &); +void passByNonConstPointerIsReinit() { + { + A a; + std::move(a); + passByNonConstPointer(&a); + a.foo(); + } + { + A a; + std::move(a); + passByNonConstReference(a); + a.foo(); + } +} + +// Passing the object through a const pointer or reference counts as a use -- +// since the called function cannot reinitialize the object. +void passByConstPointer(const A *); +void passByConstReference(const A &); +void passByConstPointerIsUse() { + { + // Declaring 'a' as const so that no ImplicitCastExpr is inserted into the + // AST -- we wouldn't want the check to rely solely on that to detect a + // const pointer argument. + const A a; + std::move(a); + passByConstPointer(&a); + // CHECK-MESSAGES: [[@LINE-1]]:25: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + } + const A a; + std::move(a); + passByConstReference(a); + // CHECK-MESSAGES: [[@LINE-1]]:24: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:3: note: move occurred here +} + +// Clearing a standard container using clear() is treated as a +// re-initialization. +void standardContainerClearIsReinit() { + { + std::string container; + std::move(container); + container.clear(); + container.empty(); + } + { + std::vector container; + std::move(container); + container.clear(); + container.empty(); + + auto container2 = container; + std::move(container2); + container2.clear(); + container2.empty(); + } + { + std::deque container; + std::move(container); + container.clear(); + container.empty(); + } + { + std::forward_list container; + std::move(container); + container.clear(); + container.empty(); + } + { + std::list container; + std::move(container); + container.clear(); + container.empty(); + } + { + std::set container; + std::move(container); + container.clear(); + container.empty(); + } + { + std::map container; + std::move(container); + container.clear(); + container.empty(); + } + { + std::multiset container; + std::move(container); + container.clear(); + container.empty(); + } + { + std::multimap container; + std::move(container); + container.clear(); + container.empty(); + } + { + std::unordered_set container; + std::move(container); + container.clear(); + container.empty(); + } + { + std::unordered_map container; + std::move(container); + container.clear(); + container.empty(); + } + { + std::unordered_multiset container; + std::move(container); + container.clear(); + container.empty(); + } + { + std::unordered_multimap container; + std::move(container); + container.clear(); + container.empty(); + } + // This should also work for typedefs of standard containers. + { + typedef std::vector IntVector; + IntVector container; + std::move(container); + container.clear(); + container.empty(); + } + // But it shouldn't work for non-standard containers. + { + // This might be called "vector", but it's not in namespace "std". + struct vector { + void clear() {} + } container; + std::move(container); + container.clear(); + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'container' used after it was + // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + } + // An intervening clear() on a different container does not reinitialize. + { + std::vector container1, container2; + std::move(container1); + container2.clear(); + container1.empty(); + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'container1' used after it was + // CHECK-MESSAGES: [[@LINE-4]]:5: note: move occurred here + } +} + +// Clearing a standard container using assign() is treated as a +// re-initialization. +void standardContainerAssignIsReinit() { + { + std::string container; + std::move(container); + container.assign(0, ' '); + container.empty(); + } + { + std::vector container; + std::move(container); + container.assign(0, 0); + container.empty(); + } + { + std::deque container; + std::move(container); + container.assign(0, 0); + container.empty(); + } + { + std::forward_list container; + std::move(container); + container.assign(0, 0); + container.empty(); + } + { + std::list container; + std::move(container); + container.clear(); + container.empty(); + } + // But it doesn't work for non-standard containers. + { + // This might be called "vector", but it's not in namespace "std". + struct vector { + void assign(std::size_t, int) {} + } container; + std::move(container); + container.assign(0, 0); + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'container' used after it was + // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + } + // An intervening assign() on a different container does not reinitialize. + { + std::vector container1, container2; + std::move(container1); + container2.assign(0, 0); + container1.empty(); + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'container1' used after it was + // CHECK-MESSAGES: [[@LINE-4]]:5: note: move occurred here + } +} + +// Resetting the standard smart pointer types using reset() is treated as a +// re-initialization. (We don't test std::weak_ptr<> because it can't be +// dereferenced directly.) +void standardSmartPointerResetIsReinit() { + { + std::unique_ptr ptr; + std::move(ptr); + ptr.reset(new A); + *ptr; + } + { + std::shared_ptr ptr; + std::move(ptr); + ptr.reset(new A); + *ptr; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Tests related to order of evaluation within expressions + +// Relative sequencing of move and use. +void passByRvalueReference(int i, A &&a); +void passByValue(int i, A a); +void passByValue(A a, int i); +A g(A, A &&); +int intFromA(A &&); +int intFromInt(int); +void sequencingOfMoveAndUse() { + // This case is fine because the move only happens inside + // passByRvalueReference(). At this point, a.getInt() is guaranteed to have + // been evaluated. + { + A a; + passByRvalueReference(a.getInt(), std::move(a)); + } + // However, if we pass by value, the move happens when the move constructor is + // called to create a temporary, and this happens before the call to + // passByValue(). Because the order in which arguments are evaluated isn't + // defined, the move may happen before the call to a.getInt(). + // + // Check that we warn about a potential use-after move for both orderings of + // a.getInt() and std::move(a), independent of the order in which the + // arguments happen to get evaluated by the compiler. + { + A a; + passByValue(a.getInt(), std::move(a)); + // CHECK-MESSAGES: [[@LINE-1]]:17: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-2]]:29: note: move occurred here + // CHECK-MESSAGES: [[@LINE-3]]:17: note: the use and move are unsequenced + } + { + A a; + passByValue(std::move(a), a.getInt()); + // CHECK-MESSAGES: [[@LINE-1]]:31: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-2]]:17: note: move occurred here + // CHECK-MESSAGES: [[@LINE-3]]:31: note: the use and move are unsequenced + } + // An even more convoluted example. + { + A a; + g(g(a, std::move(a)), g(a, std::move(a))); + // CHECK-MESSAGES: [[@LINE-1]]:9: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-2]]:27: note: move occurred here + // CHECK-MESSAGES: [[@LINE-3]]:9: note: the use and move are unsequenced + // CHECK-MESSAGES: [[@LINE-4]]:29: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-5]]:7: note: move occurred here + // CHECK-MESSAGES: [[@LINE-6]]:29: note: the use and move are unsequenced + } + // This case is fine because the actual move only happens inside the call to + // operator=(). a.getInt(), by necessity, is evaluated before that call. + { + A a; + A vec[1]; + vec[a.getInt()] = std::move(a); + } + // However, in the following case, the move happens before the assignment, and + // so the order of evaluation is not guaranteed. + { + A a; + int v[3]; + v[a.getInt()] = intFromA(std::move(a)); + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-2]]:21: note: move occurred here + // CHECK-MESSAGES: [[@LINE-3]]:7: note: the use and move are unsequenced + } + { + A a; + int v[3]; + v[intFromA(std::move(a))] = intFromInt(a.i); + // CHECK-MESSAGES: [[@LINE-1]]:44: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-2]]:7: note: move occurred here + // CHECK-MESSAGES: [[@LINE-3]]:44: note: the use and move are unsequenced + } +} + +// Relative sequencing of move and reinitialization. If the two are unsequenced, +// we conservatively assume that the move happens after the reinitialization, +// i.e. the that object does not get reinitialized after the move. +A MutateA(A a); +void passByValue(A a1, A a2); +void sequencingOfMoveAndReinit() { + // Move and reinitialization as function arguments (which are indeterminately + // sequenced). Again, check that we warn for both orderings. + { + A a; + passByValue(std::move(a), (a = A())); + a.foo(); + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:17: note: move occurred here + } + { + A a; + passByValue((a = A()), std::move(a)); + a.foo(); + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:28: note: move occurred here + } + // Common usage pattern: Move the object to a function that mutates it in some + // way, then reassign the result to the object. This pattern is fine. + { + A a; + a = MutateA(std::move(a)); + a.foo(); + } +} + +// Relative sequencing of reinitialization and use. If the two are unsequenced, +// we conservatively assume that the reinitialization happens after the use, +// i.e. that the object is not reinitialized at the point in time when it is +// used. +void sequencingOfReinitAndUse() { + // Reinitialization and use in function arguments. Again, check both possible + // orderings. + { + A a; + std::move(a); + passByValue(a.getInt(), (a = A())); + // CHECK-MESSAGES: [[@LINE-1]]:17: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + } + { + A a; + std::move(a); + passByValue((a = A()), a.getInt()); + // CHECK-MESSAGES: [[@LINE-1]]:28: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + } +} + +// The comma operator sequences its operands. +void commaOperatorSequences() { + { + A a; + A(std::move(a)) + , (a = A()); + a.foo(); + } + { + A a; + (a = A()), A(std::move(a)); + a.foo(); + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:16: note: move occurred here + } +} + +// An initializer list sequences its initialization clauses. +void initializerListSequences() { + { + struct S1 { + int i; + A a; + }; + A a; + S1 s1{a.getInt(), std::move(a)}; + } + { + struct S2 { + A a; + int i; + }; + A a; + S2 s2{std::move(a), a.getInt()}; + // CHECK-MESSAGES: [[@LINE-1]]:25: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-2]]:11: note: move occurred here + } +} + +// A declaration statement containing multiple declarations sequences the +// initializer expressions. +void declarationSequences() { + { + A a; + A a1 = a, a2 = std::move(a); + } + { + A a; + A a1 = std::move(a), a2 = a; + // CHECK-MESSAGES: [[@LINE-1]]:31: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-2]]:12: note: move occurred here + } +} + +// The logical operators && and || sequence their operands. +void logicalOperatorsSequence() { + { + A a; + if (a.getInt() > 0 && A(std::move(a)).getInt() > 0) { + A().foo(); + } + } + // A variation: Negate the result of the && (which pushes the && further down + // into the AST). + { + A a; + if (!(a.getInt() > 0 && A(std::move(a)).getInt() > 0)) { + A().foo(); + } + } + { + A a; + if (A(std::move(a)).getInt() > 0 && a.getInt() > 0) { + // CHECK-MESSAGES: [[@LINE-1]]:41: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-2]]:9: note: move occurred here + A().foo(); + } + } + { + A a; + if (a.getInt() > 0 || A(std::move(a)).getInt() > 0) { + A().foo(); + } + } + { + A a; + if (A(std::move(a)).getInt() > 0 || a.getInt() > 0) { + // CHECK-MESSAGES: [[@LINE-1]]:41: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-2]]:9: note: move occurred here + A().foo(); + } + } +} + +// A range-based for sequences the loop variable declaration before the body. +void forRangeSequences() { + A v[2] = {A(), A()}; + for (A &a : v) { + std::move(a); + } +} + +// If a variable is declared in an if statement, the declaration of the variable +// (which is treated like a reinitialization by the check) is sequenced before +// the evaluation of the condition (which constitutes a use). +void ifStmtSequencesDeclAndCondition() { + for (int i = 0; i < 10; ++i) { + if (A a = A()) { + std::move(a); + } + } +} + +namespace PR33020 { +class D { + ~D(); +}; +struct A { + D d; +}; +class B { + A a; +}; +template +class C : T, B { + void m_fn1() { + int a; + std::move(a); + C c; + } +}; +} Index: clang-tools-extra/trunk/test/clang-tidy/bugprone-virtual-near-miss.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/bugprone-virtual-near-miss.cpp +++ clang-tools-extra/trunk/test/clang-tidy/bugprone-virtual-near-miss.cpp @@ -0,0 +1,133 @@ +// RUN: %check_clang_tidy %s bugprone-virtual-near-miss %t + +class NoDefinedClass1; +class NoDefinedClass2; + +struct Base { + virtual void func(); + virtual void gunk(); + virtual ~Base(); + virtual Base &operator=(const Base &); + virtual NoDefinedClass1 *f(); +}; + +struct Derived : Base { + // Should not warn "do you want to override 'gunk'?", because gunk is already + // overriden by this class. + virtual void funk(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Derived::funk' has a similar name and the same signature as virtual method 'Base::func'; did you mean to override it? [bugprone-virtual-near-miss] + // CHECK-FIXES: virtual void func(); + + void func2(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Derived::func2' has {{.*}} 'Base::func' + // CHECK-FIXES: void func(); + + void func22(); // Should not warn. + + void gunk(); // Should not warn: gunk is override. + + void fun(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Derived::fun' has {{.*}} 'Base::func' + // CHECK-FIXES: void func(); + + Derived &operator==(const Base &); // Should not warn: operators are ignored. + + virtual NoDefinedClass2 *f1(); // Should not crash: non-defined class return type is ignored. +}; + +template +struct TBase { + virtual void tfunc(T t); +}; + +template +struct TDerived : TBase { + virtual void tfunk(T t); + // Should not apply fix for template. + // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: method 'TDerived::tfunk' has {{.*}} 'TBase::tfunc' + // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: method 'TDerived::tfunk' has {{.*}} 'TBase::tfunc' + // CHECK-FIXES: virtual void tfunk(T t); +}; + +TDerived T1; +TDerived T2; + +// Should not fix macro definition +#define MACRO1 void funcM() +// CHECK-FIXES: #define MACRO1 void funcM() +#define MACRO2(m) void m() +// CHECK-FIXES: #define MACRO2(m) void m() + +struct DerivedMacro : Base { + MACRO1; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'DerivedMacro::funcM' has {{.*}} 'Base::func' + // CHECK-FIXES: MACRO1; + + MACRO2(func3); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'DerivedMacro::func3' has {{.*}} 'Base::func' + // CHECK-FIXES: MACRO2(func); +}; + +typedef Derived derived_type; + +class Father { +public: + Father(); + virtual void func(); + virtual Father *create(int i); + virtual Base &&generate(); + virtual Base *canonical(Derived D); +}; + +class Mother { +public: + Mother(); + static void method(); + virtual int method(int argc, const char **argv); + virtual int method(int argc) const; + virtual int decay(const char *str); +}; + +class Child : private Father, private Mother { +public: + Child(); + + virtual void func2(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::func2' has {{.*}} 'Father::func' + // CHECK-FIXES: virtual void func(); + + int methoe(int x, char **strs); // Should not warn: parameter types don't match. + + int methoe(int x); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::methoe' has {{.*}} 'Mother::method' + // CHECK-FIXES: int method(int x); + + void methof(int x, const char **strs); // Should not warn: return types don't match. + + int methoh(int x, const char **strs); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::methoh' has {{.*}} 'Mother::method' + // CHECK-FIXES: int method(int x, const char **strs); + + virtual Child *creat(int i); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::creat' has {{.*}} 'Father::create' + // CHECK-FIXES: virtual Child *create(int i); + + virtual Derived &&generat(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::generat' has {{.*}} 'Father::generate' + // CHECK-FIXES: virtual Derived &&generate(); + + int decaz(const char str[]); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::decaz' has {{.*}} 'Mother::decay' + // CHECK-FIXES: int decay(const char str[]); + + operator bool(); + + derived_type *canonica(derived_type D); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::canonica' has {{.*}} 'Father::canonical' + // CHECK-FIXES: derived_type *canonical(derived_type D); + +private: + void funk(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::funk' has {{.*}} 'Father::func' + // CHECK-FIXES: void func(); +}; Index: clang-tools-extra/trunk/test/clang-tidy/cppcoreguidelines-owning-memory.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/cppcoreguidelines-owning-memory.cpp +++ clang-tools-extra/trunk/test/clang-tidy/cppcoreguidelines-owning-memory.cpp @@ -54,7 +54,7 @@ } /// FIXME: CSA finds it, but the report is misleading. Ownersemantics can catch this -/// by flow analysis similar to misc-use-after-move. +/// by flow analysis similar to bugprone-use-after-move. void csa_not_finding_leak() { gsl::owner o1 = new int(42); // Ok Index: clang-tools-extra/trunk/test/clang-tidy/misc-assert-side-effect.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/misc-assert-side-effect.cpp +++ clang-tools-extra/trunk/test/clang-tidy/misc-assert-side-effect.cpp @@ -1,114 +0,0 @@ -// RUN: %check_clang_tidy %s misc-assert-side-effect %t -- -config="{CheckOptions: [{key: misc-assert-side-effect.CheckFunctionCalls, value: 1}, {key: misc-assert-side-effect.AssertMacros, value: 'assert,assert2,my_assert,convoluted_assert,msvc_assert'}]}" -- -fexceptions - -//===--- assert definition block ------------------------------------------===// -int abort() { return 0; } - -#ifdef NDEBUG -#define assert(x) 1 -#else -#define assert(x) \ - if (!(x)) \ - (void)abort() -#endif - -void print(...); -#define assert2(e) (__builtin_expect(!(e), 0) ? \ - print (#e, __FILE__, __LINE__) : (void)0) - -#ifdef NDEBUG -#define my_assert(x) 1 -#else -#define my_assert(x) \ - ((void)((x) ? 1 : abort())) -#endif - -#ifdef NDEBUG -#define not_my_assert(x) 1 -#else -#define not_my_assert(x) \ - if (!(x)) \ - (void)abort() -#endif - -#define real_assert(x) ((void)((x) ? 1 : abort())) -#define wrap1(x) real_assert(x) -#define wrap2(x) wrap1(x) -#define convoluted_assert(x) wrap2(x) - -#define msvc_assert(expression) (void)( \ - (!!(expression)) || \ - (abort(), 0) \ - ) - - -//===----------------------------------------------------------------------===// - -class MyClass { -public: - bool badFunc(int a, int b) { return a * b > 0; } - bool goodFunc(int a, int b) const { return a * b > 0; } - - MyClass &operator=(const MyClass &rhs) { return *this; } - - int operator-() { return 1; } - - operator bool() const { return true; } - - void operator delete(void *p) {} -}; - -bool freeFunction() { - return true; -} - -int main() { - - int X = 0; - bool B = false; - assert(X == 1); - - assert(X = 1); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect [misc-assert-side-effect] - my_assert(X = 1); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found my_assert() with side effect - convoluted_assert(X = 1); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found convoluted_assert() with side effect - not_my_assert(X = 1); - - assert(++X); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect - assert(!B); - - assert(B || true); - - assert(freeFunction()); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect - - MyClass mc; - assert(mc.badFunc(0, 1)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect - assert(mc.goodFunc(0, 1)); - - MyClass mc2; - assert(mc2 = mc); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect - - assert(-mc > 0); - - MyClass *mcp; - assert(mcp = new MyClass); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect - - assert((delete mcp, false)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect - - assert((throw 1, false)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect - - assert2(1 == 2 - 1); - - msvc_assert(mc2 = mc); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found msvc_assert() with side effect - - return 0; -} Index: clang-tools-extra/trunk/test/clang-tidy/misc-bool-pointer-implicit-conversion.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/misc-bool-pointer-implicit-conversion.cpp +++ clang-tools-extra/trunk/test/clang-tidy/misc-bool-pointer-implicit-conversion.cpp @@ -1,82 +0,0 @@ -// RUN: %check_clang_tidy %s misc-bool-pointer-implicit-conversion %t - -bool *SomeFunction(); -void SomeOtherFunction(bool*); -bool F(); -void G(bool); - - -template -void t(T b) { - if (b) { - } -} - -void foo() { - bool *b = SomeFunction(); - if (b) { -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: dubious check of 'bool *' against 'nullptr' -// CHECK-FIXES: if (*b) { - } - - if (F() && b) { -// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: dubious check of 'bool *' against 'nullptr' -// CHECK-FIXES: if (F() && *b) { - } - - // TODO: warn here. - if (b) { - G(b); - } - -#define TESTMACRO if (b || F()) - - TESTMACRO { - } - - t(b); - - if (!b) { - // no-warning - } - - if (SomeFunction()) { - // no-warning - } - - bool *c = SomeFunction(); - if (c) { - (void)c; - (void)*c; // no-warning - } - - if (c) { - *c = true; // no-warning - } - - if (c) { - c[0] = false; // no-warning - } - - if (c) { - SomeOtherFunction(c); // no-warning - } - - if (c) { - delete[] c; // no-warning - } - - if (c) { - *(c) = false; // no-warning - } - - struct { - bool *b; - } d = { SomeFunction() }; - - if (d.b) - (void)*d.b; // no-warning - -#define CHECK(b) if (b) {} - CHECK(c) -} Index: clang-tools-extra/trunk/test/clang-tidy/misc-fold-init-type.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/misc-fold-init-type.cpp +++ clang-tools-extra/trunk/test/clang-tidy/misc-fold-init-type.cpp @@ -1,158 +0,0 @@ -// RUN: %check_clang_tidy %s misc-fold-init-type %t - -namespace std { -template -T accumulate(InputIt first, InputIt last, T init); - -template -T reduce(InputIt first, InputIt last, T init); -template -T reduce(ExecutionPolicy &&policy, - InputIt first, InputIt last, T init); - -struct parallel_execution_policy {}; -constexpr parallel_execution_policy par{}; - -template -T inner_product(InputIt1 first1, InputIt1 last1, - InputIt2 first2, T value); - -template -T inner_product(ExecutionPolicy &&policy, InputIt1 first1, InputIt1 last1, - InputIt2 first2, T value); - -} // namespace std - -struct FloatIterator { - typedef float value_type; -}; -template -struct TypedefTemplateIterator { typedef ValueType value_type; }; -template -struct UsingTemplateIterator { using value_type = ValueType; }; -template -struct DependentTypedefTemplateIterator { typedef typename ValueType::value_type value_type; }; -template -struct DependentUsingTemplateIterator : public TypedefTemplateIterator { using typename TypedefTemplateIterator::value_type; }; -using TypedeffedIterator = FloatIterator; - -// Positives. - -int accumulatePositive1() { - float a[1] = {0.5f}; - return std::accumulate(a, a + 1, 0); - // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int' -} - -int accumulatePositive2() { - FloatIterator it; - return std::accumulate(it, it, 0); - // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int' -} - -int accumulatePositive3() { - double a[1] = {0.0}; - return std::accumulate(a, a + 1, 0.0f); - // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'double' into type 'float' -} - -int accumulatePositive4() { - TypedefTemplateIterator it; - return std::accumulate(it, it, 0); - // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int' -} - -int accumulatePositive5() { - UsingTemplateIterator it; - return std::accumulate(it, it, 0); - // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int' -} - -int accumulatePositive6() { - DependentTypedefTemplateIterator> it; - return std::accumulate(it, it, 0); - // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int' -} - -int accumulatePositive7() { - TypedeffedIterator it; - return std::accumulate(it, it, 0); - // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int' -} - -int accumulatePositive8() { - DependentUsingTemplateIterator it; - return std::accumulate(it, it, 0); - // FIXME: this one should trigger too. -} - -int reducePositive1() { - float a[1] = {0.5f}; - return std::reduce(a, a + 1, 0); - // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int' -} - -int reducePositive2() { - float a[1] = {0.5f}; - return std::reduce(std::par, a, a + 1, 0); - // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int' -} - -int innerProductPositive1() { - float a[1] = {0.5f}; - int b[1] = {1}; - return std::inner_product(std::par, a, a + 1, b, 0); - // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int' -} - -int innerProductPositive2() { - float a[1] = {0.5f}; - int b[1] = {1}; - return std::inner_product(std::par, a, a + 1, b, 0); - // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int' -} - -// Negatives. - -int negative1() { - float a[1] = {0.5f}; - // This is OK because types match. - return std::accumulate(a, a + 1, 0.0); -} - -int negative2() { - float a[1] = {0.5f}; - // This is OK because double is bigger than float. - return std::accumulate(a, a + 1, 0.0); -} - -int negative3() { - float a[1] = {0.5f}; - // This is OK because the user explicitly specified T. - return std::accumulate(a, a + 1, 0); -} - -int negative4() { - TypedefTemplateIterator it; - // For now this is OK. - return std::accumulate(it, it, 0.0); -} - -int negative5() { - float a[1] = {0.5f}; - float b[1] = {1.0f}; - return std::inner_product(std::par, a, a + 1, b, 0.0f); -} - -namespace blah { -namespace std { -template -T accumulate(InputIt, InputIt, T); // We should not care about this one. -} - -int negative5() { - float a[1] = {0.5f}; - // Note that this is using blah::std::accumulate. - return std::accumulate(a, a + 1, 0); -} -} Index: clang-tools-extra/trunk/test/clang-tidy/misc-forward-declaration-namespace.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/misc-forward-declaration-namespace.cpp +++ clang-tools-extra/trunk/test/clang-tidy/misc-forward-declaration-namespace.cpp @@ -1,163 +0,0 @@ -// RUN: %check_clang_tidy %s misc-forward-declaration-namespace %t - -namespace { -// This is a declaration in a wrong namespace. -class T_A; -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'T_A' is never referenced, but a declaration with the same name found in another namespace 'na' [misc-forward-declaration-namespace] -// CHECK-MESSAGES: note: a declaration of 'T_A' is found here -// CHECK-MESSAGES: :[[@LINE-3]]:7: warning: no definition found for 'T_A', but a definition with the same name 'T_A' found in another namespace '(global)' [misc-forward-declaration-namespace] -// CHECK-MESSAGES: note: a definition of 'T_A' is found here -} - -namespace na { -// This is a declaration in a wrong namespace. -class T_A; -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'T_A' is never referenced, but a declaration with the same name found in another namespace '(anonymous)' -// CHECK-MESSAGES: note: a declaration of 'T_A' is found here -// CHECK-MESSAGES: :[[@LINE-3]]:7: warning: no definition found for 'T_A', but a definition with the same name 'T_A' found in another namespace '(global)' -// CHECK-MESSAGES: note: a definition of 'T_A' is found here -} - -class T_A; - -class T_A { - int x; -}; - -class NESTED; -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: no definition found for 'NESTED', but a definition with the same name 'NESTED' found in another namespace '(anonymous namespace)::nq::(anonymous)' -// CHECK-MESSAGES: note: a definition of 'NESTED' is found here - -namespace { -namespace nq { -namespace { -class NESTED {}; -} -} -} - -namespace na { -class T_B; -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'T_B' is never referenced, but a declaration with the same name found in another namespace 'nb' -// CHECK-MESSAGES: note: a declaration of 'T_B' is found here -// CHECK-MESSAGES: :[[@LINE-3]]:7: warning: no definition found for 'T_B', but a definition with the same name 'T_B' found in another namespace 'nb' -// CHECK-MESSAGES: note: a definition of 'T_B' is found here -} - -namespace nb { -class T_B; -} - -namespace nb { -class T_B { - int x; -}; -} - -namespace na { -class T_B; -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'T_B' is never referenced, but a declaration with the same name found in another namespace 'nb' -// CHECK-MESSAGES: note: a declaration of 'T_B' is found here -// CHECK-MESSAGES: :[[@LINE-3]]:7: warning: no definition found for 'T_B', but a definition with the same name 'T_B' found in another namespace 'nb' -// CHECK-MESSAGES: note: a definition of 'T_B' is found here -} - -// A simple forward declaration. Although it is never used, but no declaration -// with the same name is found in other namespace. -class OUTSIDER; - -namespace na { -// This class is referenced declaration, we don't generate warning. -class OUTSIDER_1; -} - -void f(na::OUTSIDER_1); - -namespace nc { -// This class is referenced as friend in OOP. -class OUTSIDER_1; - -class OOP { - friend struct OUTSIDER_1; -}; -} - -namespace nd { -class OUTSIDER_1; -void f(OUTSIDER_1 *); -} - -namespace nb { -class OUTSIDER_1; -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'OUTSIDER_1' is never referenced, but a declaration with the same name found in another namespace 'na' -// CHECK-MESSAGES: note: a declaration of 'OUTSIDER_1' is found here -} - - -namespace na { -template -class T_C; -} - -namespace nb { -// FIXME: this is an error, but we don't consider template class declaration -// now. -template -class T_C; -} - -namespace na { -template -class T_C { - int x; -}; -} - -namespace na { - -template -class T_TEMP { - template - struct rebind { typedef T_TEMP<_Tp1> other; }; -}; - -// We ignore class template specialization. -template class T_TEMP; -} - -namespace nb { - -template -class T_TEMP_1 { - template - struct rebind { typedef T_TEMP_1<_Tp1> other; }; -}; - -// We ignore class template specialization. -extern template class T_TEMP_1; -} - -namespace nd { -class D; -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'D' is never referenced, but a declaration with the same name found in another namespace 'nd::ne' -// CHECK-MESSAGES: note: a declaration of 'D' is found here -} - -namespace nd { -namespace ne { -class D; -} -} - -int f(nd::ne::D &d); - - -// This should be ignored by the check. -template -class Observer { - class Impl; -}; - -template -class Observer::Impl { -}; Index: clang-tools-extra/trunk/test/clang-tidy/misc-inaccurate-erase.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/misc-inaccurate-erase.cpp +++ clang-tools-extra/trunk/test/clang-tidy/misc-inaccurate-erase.cpp @@ -1,100 +0,0 @@ -// RUN: %check_clang_tidy %s misc-inaccurate-erase %t - -namespace std { -template struct vec_iterator { - T ptr; - vec_iterator operator++(int); - - template - vec_iterator(const vec_iterator &); // Omit enable_if<...>. -}; - -template struct vector { - typedef vec_iterator iterator; - - iterator begin(); - iterator end(); - - void erase(iterator); - void erase(iterator, iterator); -}; - -template struct vector_with_const_iterator { - typedef vec_iterator iterator; - typedef vec_iterator const_iterator; - - iterator begin(); - iterator end(); - - void erase(const_iterator); - void erase(const_iterator, const_iterator); -}; - -template -FwIt remove(FwIt begin, FwIt end, const T &val); - -template -FwIt remove_if(FwIt begin, FwIt end, Func f); - -template FwIt unique(FwIt begin, FwIt end); - -template struct unique_ptr {}; -} // namespace std - -struct custom_iter {}; -struct custom_container { - void erase(...); - custom_iter begin(); - custom_iter end(); -}; - -template void g() { - T t; - t.erase(std::remove(t.begin(), t.end(), 10)); - // CHECK-FIXES: {{^ }}t.erase(std::remove(t.begin(), t.end(), 10));{{$}} - - std::vector v; - v.erase(remove(v.begin(), v.end(), 10)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this call will remove at most one - // CHECK-FIXES: {{^ }}v.erase(remove(v.begin(), v.end(), 10), v.end());{{$}} -} - -#define ERASE(x, y) x.erase(remove(x.begin(), x.end(), y)) -// CHECK-FIXES: #define ERASE(x, y) x.erase(remove(x.begin(), x.end(), y)) - -int main() { - std::vector v; - - v.erase(remove(v.begin(), v.end(), 10)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this call will remove at most one item even when multiple items should be removed [misc-inaccurate-erase] - // CHECK-FIXES: {{^ }}v.erase(remove(v.begin(), v.end(), 10), v.end());{{$}} - v.erase(remove(v.begin(), v.end(), 20), v.end()); - - auto *p = &v; - p->erase(remove(p->begin(), p->end(), 11)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this call will remove at most one - // CHECK-FIXES: {{^ }}p->erase(remove(p->begin(), p->end(), 11), p->end());{{$}} - - std::vector_with_const_iterator v2; - v2.erase(remove(v2.begin(), v2.end(), 12)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this call will remove at most one - // CHECK-FIXES: {{^ }}v2.erase(remove(v2.begin(), v2.end(), 12), v2.end());{{$}} - - // Fix is not trivial. - auto it = v.end(); - v.erase(remove(v.begin(), it, 10)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this call will remove at most one - // CHECK-FIXES: {{^ }}v.erase(remove(v.begin(), it, 10));{{$}} - - g>(); - g(); - - ERASE(v, 15); - // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: this call will remove at most one - // CHECK-FIXES: {{^ }}ERASE(v, 15);{{$}} - - std::vector> vupi; - auto iter = vupi.begin(); - vupi.erase(iter++); - // CHECK-FIXES: {{^ }}vupi.erase(iter++);{{$}} -} Index: clang-tools-extra/trunk/test/clang-tidy/misc-move-forwarding-reference.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/misc-move-forwarding-reference.cpp +++ clang-tools-extra/trunk/test/clang-tidy/misc-move-forwarding-reference.cpp @@ -1,125 +0,0 @@ -// RUN: %check_clang_tidy %s misc-move-forwarding-reference %t -- -- -std=c++14 -fno-delayed-template-parsing - -namespace std { -template struct remove_reference; - -template struct remove_reference { typedef _Tp type; }; - -template struct remove_reference<_Tp &> { typedef _Tp type; }; - -template struct remove_reference<_Tp &&> { typedef _Tp type; }; - -template -constexpr typename std::remove_reference<_Tp>::type &&move(_Tp &&__t); - -} // namespace std - -// Standard case. -template void f1(U &&SomeU) { - T SomeT(std::move(SomeU)); - // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to - // CHECK-FIXES: T SomeT(std::forward(SomeU)); -} - -// Ignore parentheses around the argument to std::move(). -template void f2(U &&SomeU) { - T SomeT(std::move((SomeU))); - // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to - // CHECK-FIXES: T SomeT(std::forward((SomeU))); -} - -// Handle the case correctly where std::move() is being used through a using -// declaration. -template void f3(U &&SomeU) { - using std::move; - T SomeT(move(SomeU)); - // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to - // CHECK-FIXES: T SomeT(std::forward(SomeU)); -} - -// Handle the case correctly where a global specifier is prepended to -// std::move(). -template void f4(U &&SomeU) { - T SomeT(::std::move(SomeU)); - // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to - // CHECK-FIXES: T SomeT(::std::forward(SomeU)); -} - -// Create a correct fix if there are spaces around the scope resolution -// operator. -template void f5(U &&SomeU) { - { - T SomeT(:: std :: move(SomeU)); - // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: forwarding reference passed to - // CHECK-FIXES: T SomeT(::std::forward(SomeU)); - } - { - T SomeT(std :: move(SomeU)); - // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: forwarding reference passed to - // CHECK-FIXES: T SomeT(std::forward(SomeU)); - } -} - -// Ignore const rvalue reference parameters. -template void f6(const U &&SomeU) { - T SomeT(std::move(SomeU)); -} - -// Ignore the case where the argument to std::move() is a lambda parameter (and -// thus not actually a parameter of the function template). -template void f7() { - [](U &&SomeU) { T SomeT(std::move(SomeU)); }; -} - -// Ignore the case where the argument is a lvalue reference. -template void f8(U &SomeU) { - T SomeT(std::move(SomeU)); -} - -// Ignore the case where the template parameter is a class template parameter -// (i.e. no template argument deduction is taking place). -template class SomeClass { - void f(U &&SomeU) { T SomeT(std::move(SomeU)); } -}; - -// Ignore the case where the function parameter in the template isn't an rvalue -// reference but the template argument is explicitly set to be an rvalue -// reference. -class A {}; -template void foo(T); -void f8() { - A a; - foo(std::move(a)); -} - -// A warning is output, but no fix is suggested, if a macro is used to rename -// std::move. -#define MOVE(x) std::move((x)) -template void f9(U &&SomeU) { - T SomeT(MOVE(SomeU)); - // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to -} - -// Same result if the argument is passed outside of the macro. -#undef MOVE -#define MOVE std::move -template void f10(U &&SomeU) { - T SomeT(MOVE(SomeU)); - // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to -} - -// Same result if the macro does not include the "std" namespace. -#undef MOVE -#define MOVE move -template void f11(U &&SomeU) { - T SomeT(std::MOVE(SomeU)); - // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to -} - -// Handle the case correctly where the forwarding reference is a parameter of a -// generic lambda. -template void f12() { - [] (auto&& x) { T SomeT(std::move(x)); }; - // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: forwarding reference passed to - // CHECK-FIXES: [] (auto&& x) { T SomeT(std::forward(x)); } -} Index: clang-tools-extra/trunk/test/clang-tidy/misc-multiple-statement-macro.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/misc-multiple-statement-macro.cpp +++ clang-tools-extra/trunk/test/clang-tidy/misc-multiple-statement-macro.cpp @@ -1,85 +0,0 @@ -// RUN: %check_clang_tidy %s misc-multiple-statement-macro %t - -void F(); - -#define BAD_MACRO(x) \ - F(); \ - F() - -#define GOOD_MACRO(x) \ - do { \ - F(); \ - F(); \ - } while (0) - -#define GOOD_MACRO2(x) F() - -#define GOOD_MACRO3(x) F(); - -#define MACRO_ARG_MACRO(X) \ - if (54) \ - X(2) - -#define ALL_IN_MACRO(X) \ - if (43) \ - F(); \ - F() - -#define GOOD_NESTED(x) \ - if (x) \ - GOOD_MACRO3(x); \ - F(); - -#define IF(x) if(x) - -void positives() { - if (1) - BAD_MACRO(1); - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple statement macro used without braces; some statements will be unconditionally executed [misc-multiple-statement-macro] - if (1) { - } else - BAD_MACRO(1); - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple statement macro used - while (1) - BAD_MACRO(1); - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple statement macro used - for (;;) - BAD_MACRO(1); - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple statement macro used - - MACRO_ARG_MACRO(BAD_MACRO); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: multiple statement macro used - MACRO_ARG_MACRO(F(); int); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: multiple statement macro used - IF(1) BAD_MACRO(1); - // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: multiple statement macro used -} - -void negatives() { - if (1) { - BAD_MACRO(1); - } else { - BAD_MACRO(1); - } - while (1) { - BAD_MACRO(1); - } - for (;;) { - BAD_MACRO(1); - } - - if (1) - GOOD_MACRO(1); - if (1) { - GOOD_MACRO(1); - } - if (1) - GOOD_MACRO2(1); - if (1) - GOOD_MACRO3(1); - - MACRO_ARG_MACRO(GOOD_MACRO); - ALL_IN_MACRO(1); - - IF(1) GOOD_MACRO(1); -} Index: clang-tools-extra/trunk/test/clang-tidy/misc-use-after-move.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/misc-use-after-move.cpp +++ clang-tools-extra/trunk/test/clang-tidy/misc-use-after-move.cpp @@ -1,1164 +0,0 @@ -// RUN: %check_clang_tidy %s misc-use-after-move %t -- -- -std=c++11 -fno-delayed-template-parsing - -typedef decltype(nullptr) nullptr_t; - -namespace std { -typedef unsigned size_t; - -template -struct unique_ptr { - unique_ptr(); - T *get() const; - explicit operator bool() const; - void reset(T *ptr); - T &operator*() const; - T *operator->() const; - T& operator[](size_t i) const; -}; - -template -struct shared_ptr { - shared_ptr(); - T *get() const; - explicit operator bool() const; - void reset(T *ptr); - T &operator*() const; - T *operator->() const; -}; - -template -struct weak_ptr { - weak_ptr(); - bool expired() const; -}; - -#define DECLARE_STANDARD_CONTAINER(name) \ - template \ - struct name { \ - name(); \ - void clear(); \ - bool empty(); \ - } - -#define DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(name) \ - template \ - struct name { \ - name(); \ - void clear(); \ - bool empty(); \ - void assign(size_t, const T &); \ - } - -DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(basic_string); -DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(vector); -DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(deque); -DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(forward_list); -DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(list); -DECLARE_STANDARD_CONTAINER(set); -DECLARE_STANDARD_CONTAINER(map); -DECLARE_STANDARD_CONTAINER(multiset); -DECLARE_STANDARD_CONTAINER(multimap); -DECLARE_STANDARD_CONTAINER(unordered_set); -DECLARE_STANDARD_CONTAINER(unordered_map); -DECLARE_STANDARD_CONTAINER(unordered_multiset); -DECLARE_STANDARD_CONTAINER(unordered_multimap); - -typedef basic_string string; - -template -struct remove_reference; - -template -struct remove_reference { - typedef _Tp type; -}; - -template -struct remove_reference<_Tp &> { - typedef _Tp type; -}; - -template -struct remove_reference<_Tp &&> { - typedef _Tp type; -}; - -template -constexpr typename std::remove_reference<_Tp>::type &&move(_Tp &&__t) noexcept { - return static_cast::type &&>(__t); -} - -} // namespace std - -class A { -public: - A(); - A(const A &); - A(A &&); - - A &operator=(const A &); - A &operator=(A &&); - - void foo() const; - int getInt() const; - - operator bool() const; - - int i; -}; - -//////////////////////////////////////////////////////////////////////////////// -// General tests. - -// Simple case. -void simple() { - A a; - a.foo(); - A other_a = std::move(a); - a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:15: note: move occurred here -} - -// A warning should only be emitted for one use-after-move. -void onlyFlagOneUseAfterMove() { - A a; - a.foo(); - A other_a = std::move(a); - a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:15: note: move occurred here - a.foo(); -} - -void moveAfterMove() { - // Move-after-move also counts as a use. - { - A a; - std::move(a); - std::move(a); - // CHECK-MESSAGES: [[@LINE-1]]:15: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here - } - // This is also true if the move itself turns into the use on the second loop - // iteration. - { - A a; - for (int i = 0; i < 10; ++i) { - std::move(a); - // CHECK-MESSAGES: [[@LINE-1]]:17: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-2]]:7: note: move occurred here - // CHECK-MESSAGES: [[@LINE-3]]:17: note: the use happens in a later loop - } - } -} - -// Checks also works on function parameters that have a use-after move. -void parameters(A a) { - std::move(a); - a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:3: note: move occurred here -} - -void standardSmartPtr() { - // std::unique_ptr<>, std::shared_ptr<> and std::weak_ptr<> are guaranteed to - // be null after a std::move. So the check only flags accesses that would - // dereference the pointer. - { - std::unique_ptr ptr; - std::move(ptr); - ptr.get(); - static_cast(ptr); - *ptr; - // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'ptr' used after it was moved - // CHECK-MESSAGES: [[@LINE-5]]:5: note: move occurred here - } - { - std::unique_ptr ptr; - std::move(ptr); - ptr->foo(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here - } - { - std::unique_ptr ptr; - std::move(ptr); - ptr[0]; - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here - } - { - std::shared_ptr ptr; - std::move(ptr); - ptr.get(); - static_cast(ptr); - *ptr; - // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'ptr' used after it was moved - // CHECK-MESSAGES: [[@LINE-5]]:5: note: move occurred here - } - { - std::shared_ptr ptr; - std::move(ptr); - ptr->foo(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here - } - { - // std::weak_ptr<> cannot be dereferenced directly, so we only check that - // member functions may be called on it after a move. - std::weak_ptr ptr; - std::move(ptr); - ptr.expired(); - } - // Make sure we recognize std::unique_ptr<> or std::shared_ptr<> if they're - // wrapped in a typedef. - { - typedef std::unique_ptr PtrToA; - PtrToA ptr; - std::move(ptr); - ptr.get(); - } - { - typedef std::shared_ptr PtrToA; - PtrToA ptr; - std::move(ptr); - ptr.get(); - } - // And we don't get confused if the template argument is a little more - // involved. - { - struct B { - typedef A AnotherNameForA; - }; - std::unique_ptr ptr; - std::move(ptr); - ptr.get(); - } - // We don't give any special treatment to types that are called "unique_ptr" - // or "shared_ptr" but are not in the "::std" namespace. - { - struct unique_ptr { - void get(); - } ptr; - std::move(ptr); - ptr.get(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here - } -} - -// The check also works in member functions. -class Container { - void useAfterMoveInMemberFunction() { - A a; - std::move(a); - a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here - } -}; - -// We see the std::move() if it's inside a declaration. -void moveInDeclaration() { - A a; - A another_a(std::move(a)); - a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here -} - -// We see the std::move if it's inside an initializer list. Initializer lists -// are a special case because they cause ASTContext::getParents() to return -// multiple parents for certain nodes in their subtree. This is because -// RecursiveASTVisitor visits both the syntactic and semantic forms of -// InitListExpr, and the parent-child relationships are different between the -// two forms. -void moveInInitList() { - struct S { - A a; - }; - A a; - S s{std::move(a)}; - a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:7: note: move occurred here -} - -void lambdas() { - // Use-after-moves inside a lambda should be detected. - { - A a; - auto lambda = [a] { - std::move(a); - a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:7: note: move occurred here - }; - } - // This is just as true if the variable was declared inside the lambda. - { - auto lambda = [] { - A a; - std::move(a); - a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:7: note: move occurred here - }; - } - // But don't warn if the move happened inside the lambda but the use happened - // outside -- because - // - the 'a' inside the lambda is a copy, and - // - we don't know when the lambda will get called anyway - { - A a; - auto lambda = [a] { - std::move(a); - }; - a.foo(); - } - // Warn if the use consists of a capture that happens after a move. - { - A a; - std::move(a); - auto lambda = [a]() { a.foo(); }; - // CHECK-MESSAGES: [[@LINE-1]]:20: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here - } - // ...even if the capture was implicit. - { - A a; - std::move(a); - auto lambda = [=]() { a.foo(); }; - // CHECK-MESSAGES: [[@LINE-1]]:27: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here - } - // Same tests but for capture by reference. - { - A a; - std::move(a); - auto lambda = [&a]() { a.foo(); }; - // CHECK-MESSAGES: [[@LINE-1]]:21: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here - } - { - A a; - std::move(a); - auto lambda = [&]() { a.foo(); }; - // CHECK-MESSAGES: [[@LINE-1]]:27: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here - } - // But don't warn if the move happened after the capture. - { - A a; - auto lambda = [a]() { a.foo(); }; - std::move(a); - } - // ...and again, same thing with an implicit move. - { - A a; - auto lambda = [=]() { a.foo(); }; - std::move(a); - } - // Same tests but for capture by reference. - { - A a; - auto lambda = [&a]() { a.foo(); }; - std::move(a); - } - { - A a; - auto lambda = [&]() { a.foo(); }; - std::move(a); - } -} - -// Use-after-moves are detected in uninstantiated templates if the moved type -// is not a dependent type. -template -void movedTypeIsNotDependentType() { - T t; - A a; - std::move(a); - a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:3: note: move occurred here -} - -// And if the moved type is a dependent type, the use-after-move is detected if -// the template is instantiated. -template -void movedTypeIsDependentType() { - T t; - std::move(t); - t.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 't' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:3: note: move occurred here -} -template void movedTypeIsDependentType(); - -// We handle the case correctly where the move consists of an implicit call -// to a conversion operator. -void implicitConversionOperator() { - struct Convertible { - operator A() && { return A(); } - }; - void takeA(A a); - - Convertible convertible; - takeA(std::move(convertible)); - convertible; - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'convertible' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:9: note: move occurred here -} - -// Using decltype on an expression is not a use. -void decltypeIsNotUse() { - A a; - std::move(a); - decltype(a) other_a; -} - -// Ignore moves or uses that occur as part of template arguments. -template -class ClassTemplate { -public: - void foo(A a); -}; -template -void functionTemplate(A a); -void templateArgIsNotUse() { - { - // A pattern like this occurs in the EXPECT_EQ and ASSERT_EQ macros in - // Google Test. - A a; - ClassTemplate().foo(std::move(a)); - } - { - A a; - functionTemplate(std::move(a)); - } -} - -// Ignore moves of global variables. -A global_a; -void ignoreGlobalVariables() { - std::move(global_a); - global_a.foo(); -} - -// Ignore moves of member variables. -class IgnoreMemberVariables { - A a; - static A static_a; - - void f() { - std::move(a); - a.foo(); - - std::move(static_a); - static_a.foo(); - } -}; - -//////////////////////////////////////////////////////////////////////////////// -// Tests involving control flow. - -void useAndMoveInLoop() { - // Warn about use-after-moves if they happen in a later loop iteration than - // the std::move(). - { - A a; - for (int i = 0; i < 10; ++i) { - a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE+2]]:7: note: move occurred here - // CHECK-MESSAGES: [[@LINE-3]]:7: note: the use happens in a later loop - std::move(a); - } - } - // However, this case shouldn't be flagged -- the scope of the declaration of - // 'a' is important. - { - for (int i = 0; i < 10; ++i) { - A a; - a.foo(); - std::move(a); - } - } - // Same as above, except that we have an unrelated variable being declared in - // the same declaration as 'a'. This case is interesting because it tests that - // the synthetic DeclStmts generated by the CFG are sequenced correctly - // relative to the other statements. - { - for (int i = 0; i < 10; ++i) { - A a, other; - a.foo(); - std::move(a); - } - } - // Don't warn if we return after the move. - { - A a; - for (int i = 0; i < 10; ++i) { - a.foo(); - if (a.getInt() > 0) { - std::move(a); - return; - } - } - } -} - -void differentBranches(int i) { - // Don't warn if the use is in a different branch from the move. - { - A a; - if (i > 0) { - std::move(a); - } else { - a.foo(); - } - } - // Same thing, but with a ternary operator. - { - A a; - i > 0 ? (void)std::move(a) : a.foo(); - } - // A variation on the theme above. - { - A a; - a.getInt() > 0 ? a.getInt() : A(std::move(a)).getInt(); - } - // Same thing, but with a switch statement. - { - A a; - switch (i) { - case 1: - std::move(a); - break; - case 2: - a.foo(); - break; - } - } - // However, if there's a fallthrough, we do warn. - { - A a; - switch (i) { - case 1: - std::move(a); - case 2: - a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-4]]:7: note: move occurred here - break; - } - } -} - -// False positive: A use-after-move is flagged even though the "if (b)" and -// "if (!b)" are mutually exclusive. -void mutuallyExclusiveBranchesFalsePositive(bool b) { - A a; - if (b) { - std::move(a); - } - if (!b) { - a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-5]]:5: note: move occurred here - } -} - -// Destructors marked [[noreturn]] are handled correctly in the control flow -// analysis. (These are used in some styles of assertion macros.) -class FailureLogger { -public: - FailureLogger(); - [[noreturn]] ~FailureLogger(); - void log(const char *); -}; -#define ASSERT(x) \ - while (x) \ - FailureLogger().log(#x) -bool operationOnA(A); -void noreturnDestructor() { - A a; - // The while loop in the ASSERT() would ordinarily have the potential to cause - // a use-after-move because the second iteration of the loop would be using a - // variable that had been moved from in the first iteration. Check that the - // CFG knows that the second iteration of the loop is never reached because - // the FailureLogger destructor is marked [[noreturn]]. - ASSERT(operationOnA(std::move(a))); -} -#undef ASSERT - -//////////////////////////////////////////////////////////////////////////////// -// Tests for reinitializations - -template -void swap(T &a, T &b) { - T tmp = std::move(a); - a = std::move(b); - b = std::move(tmp); -} -void assignments(int i) { - // Don't report a use-after-move if the variable was assigned to in the - // meantime. - { - A a; - std::move(a); - a = A(); - a.foo(); - } - // The assignment should also be recognized if move, assignment and use don't - // all happen in the same block (but the assignment is still guaranteed to - // prevent a use-after-move). - { - A a; - if (i == 1) { - std::move(a); - a = A(); - } - if (i == 2) { - a.foo(); - } - } - { - A a; - if (i == 1) { - std::move(a); - } - if (i == 2) { - a = A(); - a.foo(); - } - } - // The built-in assignment operator should also be recognized as a - // reinitialization. (std::move() may be called on built-in types in template - // code.) - { - int a1 = 1, a2 = 2; - swap(a1, a2); - } - // A std::move() after the assignment makes the variable invalid again. - { - A a; - std::move(a); - a = A(); - std::move(a); - a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here - } - // Report a use-after-move if we can't be sure that the variable was assigned - // to. - { - A a; - std::move(a); - if (i < 10) { - a = A(); - } - if (i > 5) { - a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-7]]:5: note: move occurred here - } - } -} - -// Passing the object to a function through a non-const pointer or reference -// counts as a re-initialization. -void passByNonConstPointer(A *); -void passByNonConstReference(A &); -void passByNonConstPointerIsReinit() { - { - A a; - std::move(a); - passByNonConstPointer(&a); - a.foo(); - } - { - A a; - std::move(a); - passByNonConstReference(a); - a.foo(); - } -} - -// Passing the object through a const pointer or reference counts as a use -- -// since the called function cannot reinitialize the object. -void passByConstPointer(const A *); -void passByConstReference(const A &); -void passByConstPointerIsUse() { - { - // Declaring 'a' as const so that no ImplicitCastExpr is inserted into the - // AST -- we wouldn't want the check to rely solely on that to detect a - // const pointer argument. - const A a; - std::move(a); - passByConstPointer(&a); - // CHECK-MESSAGES: [[@LINE-1]]:25: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here - } - const A a; - std::move(a); - passByConstReference(a); - // CHECK-MESSAGES: [[@LINE-1]]:24: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:3: note: move occurred here -} - -// Clearing a standard container using clear() is treated as a -// re-initialization. -void standardContainerClearIsReinit() { - { - std::string container; - std::move(container); - container.clear(); - container.empty(); - } - { - std::vector container; - std::move(container); - container.clear(); - container.empty(); - - auto container2 = container; - std::move(container2); - container2.clear(); - container2.empty(); - } - { - std::deque container; - std::move(container); - container.clear(); - container.empty(); - } - { - std::forward_list container; - std::move(container); - container.clear(); - container.empty(); - } - { - std::list container; - std::move(container); - container.clear(); - container.empty(); - } - { - std::set container; - std::move(container); - container.clear(); - container.empty(); - } - { - std::map container; - std::move(container); - container.clear(); - container.empty(); - } - { - std::multiset container; - std::move(container); - container.clear(); - container.empty(); - } - { - std::multimap container; - std::move(container); - container.clear(); - container.empty(); - } - { - std::unordered_set container; - std::move(container); - container.clear(); - container.empty(); - } - { - std::unordered_map container; - std::move(container); - container.clear(); - container.empty(); - } - { - std::unordered_multiset container; - std::move(container); - container.clear(); - container.empty(); - } - { - std::unordered_multimap container; - std::move(container); - container.clear(); - container.empty(); - } - // This should also work for typedefs of standard containers. - { - typedef std::vector IntVector; - IntVector container; - std::move(container); - container.clear(); - container.empty(); - } - // But it shouldn't work for non-standard containers. - { - // This might be called "vector", but it's not in namespace "std". - struct vector { - void clear() {} - } container; - std::move(container); - container.clear(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'container' used after it was - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here - } - // An intervening clear() on a different container does not reinitialize. - { - std::vector container1, container2; - std::move(container1); - container2.clear(); - container1.empty(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'container1' used after it was - // CHECK-MESSAGES: [[@LINE-4]]:5: note: move occurred here - } -} - -// Clearing a standard container using assign() is treated as a -// re-initialization. -void standardContainerAssignIsReinit() { - { - std::string container; - std::move(container); - container.assign(0, ' '); - container.empty(); - } - { - std::vector container; - std::move(container); - container.assign(0, 0); - container.empty(); - } - { - std::deque container; - std::move(container); - container.assign(0, 0); - container.empty(); - } - { - std::forward_list container; - std::move(container); - container.assign(0, 0); - container.empty(); - } - { - std::list container; - std::move(container); - container.clear(); - container.empty(); - } - // But it doesn't work for non-standard containers. - { - // This might be called "vector", but it's not in namespace "std". - struct vector { - void assign(std::size_t, int) {} - } container; - std::move(container); - container.assign(0, 0); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'container' used after it was - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here - } - // An intervening assign() on a different container does not reinitialize. - { - std::vector container1, container2; - std::move(container1); - container2.assign(0, 0); - container1.empty(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'container1' used after it was - // CHECK-MESSAGES: [[@LINE-4]]:5: note: move occurred here - } -} - -// Resetting the standard smart pointer types using reset() is treated as a -// re-initialization. (We don't test std::weak_ptr<> because it can't be -// dereferenced directly.) -void standardSmartPointerResetIsReinit() { - { - std::unique_ptr ptr; - std::move(ptr); - ptr.reset(new A); - *ptr; - } - { - std::shared_ptr ptr; - std::move(ptr); - ptr.reset(new A); - *ptr; - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Tests related to order of evaluation within expressions - -// Relative sequencing of move and use. -void passByRvalueReference(int i, A &&a); -void passByValue(int i, A a); -void passByValue(A a, int i); -A g(A, A &&); -int intFromA(A &&); -int intFromInt(int); -void sequencingOfMoveAndUse() { - // This case is fine because the move only happens inside - // passByRvalueReference(). At this point, a.getInt() is guaranteed to have - // been evaluated. - { - A a; - passByRvalueReference(a.getInt(), std::move(a)); - } - // However, if we pass by value, the move happens when the move constructor is - // called to create a temporary, and this happens before the call to - // passByValue(). Because the order in which arguments are evaluated isn't - // defined, the move may happen before the call to a.getInt(). - // - // Check that we warn about a potential use-after move for both orderings of - // a.getInt() and std::move(a), independent of the order in which the - // arguments happen to get evaluated by the compiler. - { - A a; - passByValue(a.getInt(), std::move(a)); - // CHECK-MESSAGES: [[@LINE-1]]:17: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-2]]:29: note: move occurred here - // CHECK-MESSAGES: [[@LINE-3]]:17: note: the use and move are unsequenced - } - { - A a; - passByValue(std::move(a), a.getInt()); - // CHECK-MESSAGES: [[@LINE-1]]:31: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-2]]:17: note: move occurred here - // CHECK-MESSAGES: [[@LINE-3]]:31: note: the use and move are unsequenced - } - // An even more convoluted example. - { - A a; - g(g(a, std::move(a)), g(a, std::move(a))); - // CHECK-MESSAGES: [[@LINE-1]]:9: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-2]]:27: note: move occurred here - // CHECK-MESSAGES: [[@LINE-3]]:9: note: the use and move are unsequenced - // CHECK-MESSAGES: [[@LINE-4]]:29: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-5]]:7: note: move occurred here - // CHECK-MESSAGES: [[@LINE-6]]:29: note: the use and move are unsequenced - } - // This case is fine because the actual move only happens inside the call to - // operator=(). a.getInt(), by necessity, is evaluated before that call. - { - A a; - A vec[1]; - vec[a.getInt()] = std::move(a); - } - // However, in the following case, the move happens before the assignment, and - // so the order of evaluation is not guaranteed. - { - A a; - int v[3]; - v[a.getInt()] = intFromA(std::move(a)); - // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-2]]:21: note: move occurred here - // CHECK-MESSAGES: [[@LINE-3]]:7: note: the use and move are unsequenced - } - { - A a; - int v[3]; - v[intFromA(std::move(a))] = intFromInt(a.i); - // CHECK-MESSAGES: [[@LINE-1]]:44: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-2]]:7: note: move occurred here - // CHECK-MESSAGES: [[@LINE-3]]:44: note: the use and move are unsequenced - } -} - -// Relative sequencing of move and reinitialization. If the two are unsequenced, -// we conservatively assume that the move happens after the reinitialization, -// i.e. the that object does not get reinitialized after the move. -A MutateA(A a); -void passByValue(A a1, A a2); -void sequencingOfMoveAndReinit() { - // Move and reinitialization as function arguments (which are indeterminately - // sequenced). Again, check that we warn for both orderings. - { - A a; - passByValue(std::move(a), (a = A())); - a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:17: note: move occurred here - } - { - A a; - passByValue((a = A()), std::move(a)); - a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:28: note: move occurred here - } - // Common usage pattern: Move the object to a function that mutates it in some - // way, then reassign the result to the object. This pattern is fine. - { - A a; - a = MutateA(std::move(a)); - a.foo(); - } -} - -// Relative sequencing of reinitialization and use. If the two are unsequenced, -// we conservatively assume that the reinitialization happens after the use, -// i.e. that the object is not reinitialized at the point in time when it is -// used. -void sequencingOfReinitAndUse() { - // Reinitialization and use in function arguments. Again, check both possible - // orderings. - { - A a; - std::move(a); - passByValue(a.getInt(), (a = A())); - // CHECK-MESSAGES: [[@LINE-1]]:17: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here - } - { - A a; - std::move(a); - passByValue((a = A()), a.getInt()); - // CHECK-MESSAGES: [[@LINE-1]]:28: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here - } -} - -// The comma operator sequences its operands. -void commaOperatorSequences() { - { - A a; - A(std::move(a)) - , (a = A()); - a.foo(); - } - { - A a; - (a = A()), A(std::move(a)); - a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:16: note: move occurred here - } -} - -// An initializer list sequences its initialization clauses. -void initializerListSequences() { - { - struct S1 { - int i; - A a; - }; - A a; - S1 s1{a.getInt(), std::move(a)}; - } - { - struct S2 { - A a; - int i; - }; - A a; - S2 s2{std::move(a), a.getInt()}; - // CHECK-MESSAGES: [[@LINE-1]]:25: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-2]]:11: note: move occurred here - } -} - -// A declaration statement containing multiple declarations sequences the -// initializer expressions. -void declarationSequences() { - { - A a; - A a1 = a, a2 = std::move(a); - } - { - A a; - A a1 = std::move(a), a2 = a; - // CHECK-MESSAGES: [[@LINE-1]]:31: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-2]]:12: note: move occurred here - } -} - -// The logical operators && and || sequence their operands. -void logicalOperatorsSequence() { - { - A a; - if (a.getInt() > 0 && A(std::move(a)).getInt() > 0) { - A().foo(); - } - } - // A variation: Negate the result of the && (which pushes the && further down - // into the AST). - { - A a; - if (!(a.getInt() > 0 && A(std::move(a)).getInt() > 0)) { - A().foo(); - } - } - { - A a; - if (A(std::move(a)).getInt() > 0 && a.getInt() > 0) { - // CHECK-MESSAGES: [[@LINE-1]]:41: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-2]]:9: note: move occurred here - A().foo(); - } - } - { - A a; - if (a.getInt() > 0 || A(std::move(a)).getInt() > 0) { - A().foo(); - } - } - { - A a; - if (A(std::move(a)).getInt() > 0 || a.getInt() > 0) { - // CHECK-MESSAGES: [[@LINE-1]]:41: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-2]]:9: note: move occurred here - A().foo(); - } - } -} - -// A range-based for sequences the loop variable declaration before the body. -void forRangeSequences() { - A v[2] = {A(), A()}; - for (A &a : v) { - std::move(a); - } -} - -// If a variable is declared in an if statement, the declaration of the variable -// (which is treated like a reinitialization by the check) is sequenced before -// the evaluation of the condition (which constitutes a use). -void ifStmtSequencesDeclAndCondition() { - for (int i = 0; i < 10; ++i) { - if (A a = A()) { - std::move(a); - } - } -} - -namespace PR33020 { -class D { - ~D(); -}; -struct A { - D d; -}; -class B { - A a; -}; -template -class C : T, B { - void m_fn1() { - int a; - std::move(a); - C c; - } -}; -} Index: clang-tools-extra/trunk/test/clang-tidy/misc-virtual-near-miss.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/misc-virtual-near-miss.cpp +++ clang-tools-extra/trunk/test/clang-tidy/misc-virtual-near-miss.cpp @@ -1,133 +0,0 @@ -// RUN: %check_clang_tidy %s misc-virtual-near-miss %t - -class NoDefinedClass1; -class NoDefinedClass2; - -struct Base { - virtual void func(); - virtual void gunk(); - virtual ~Base(); - virtual Base &operator=(const Base &); - virtual NoDefinedClass1 *f(); -}; - -struct Derived : Base { - // Should not warn "do you want to override 'gunk'?", because gunk is already - // overriden by this class. - virtual void funk(); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Derived::funk' has a similar name and the same signature as virtual method 'Base::func'; did you mean to override it? [misc-virtual-near-miss] - // CHECK-FIXES: virtual void func(); - - void func2(); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Derived::func2' has {{.*}} 'Base::func' - // CHECK-FIXES: void func(); - - void func22(); // Should not warn. - - void gunk(); // Should not warn: gunk is override. - - void fun(); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Derived::fun' has {{.*}} 'Base::func' - // CHECK-FIXES: void func(); - - Derived &operator==(const Base &); // Should not warn: operators are ignored. - - virtual NoDefinedClass2 *f1(); // Should not crash: non-defined class return type is ignored. -}; - -template -struct TBase { - virtual void tfunc(T t); -}; - -template -struct TDerived : TBase { - virtual void tfunk(T t); - // Should not apply fix for template. - // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: method 'TDerived::tfunk' has {{.*}} 'TBase::tfunc' - // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: method 'TDerived::tfunk' has {{.*}} 'TBase::tfunc' - // CHECK-FIXES: virtual void tfunk(T t); -}; - -TDerived T1; -TDerived T2; - -// Should not fix macro definition -#define MACRO1 void funcM() -// CHECK-FIXES: #define MACRO1 void funcM() -#define MACRO2(m) void m() -// CHECK-FIXES: #define MACRO2(m) void m() - -struct DerivedMacro : Base { - MACRO1; - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'DerivedMacro::funcM' has {{.*}} 'Base::func' - // CHECK-FIXES: MACRO1; - - MACRO2(func3); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'DerivedMacro::func3' has {{.*}} 'Base::func' - // CHECK-FIXES: MACRO2(func); -}; - -typedef Derived derived_type; - -class Father { -public: - Father(); - virtual void func(); - virtual Father *create(int i); - virtual Base &&generate(); - virtual Base *canonical(Derived D); -}; - -class Mother { -public: - Mother(); - static void method(); - virtual int method(int argc, const char **argv); - virtual int method(int argc) const; - virtual int decay(const char *str); -}; - -class Child : private Father, private Mother { -public: - Child(); - - virtual void func2(); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::func2' has {{.*}} 'Father::func' - // CHECK-FIXES: virtual void func(); - - int methoe(int x, char **strs); // Should not warn: parameter types don't match. - - int methoe(int x); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::methoe' has {{.*}} 'Mother::method' - // CHECK-FIXES: int method(int x); - - void methof(int x, const char **strs); // Should not warn: return types don't match. - - int methoh(int x, const char **strs); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::methoh' has {{.*}} 'Mother::method' - // CHECK-FIXES: int method(int x, const char **strs); - - virtual Child *creat(int i); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::creat' has {{.*}} 'Father::create' - // CHECK-FIXES: virtual Child *create(int i); - - virtual Derived &&generat(); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::generat' has {{.*}} 'Father::generate' - // CHECK-FIXES: virtual Derived &&generate(); - - int decaz(const char str[]); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::decaz' has {{.*}} 'Mother::decay' - // CHECK-FIXES: int decay(const char str[]); - - operator bool(); - - derived_type *canonica(derived_type D); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::canonica' has {{.*}} 'Father::canonical' - // CHECK-FIXES: derived_type *canonical(derived_type D); - -private: - void funk(); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::funk' has {{.*}} 'Father::func' - // CHECK-FIXES: void func(); -};