Index: clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/CMakeLists.txt +++ clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/CMakeLists.txt @@ -4,6 +4,7 @@ CppCoreGuidelinesTidyModule.cpp ProTypeConstCastCheck.cpp ProTypeReinterpretCastCheck.cpp + ProTypeStaticCastDowncastCheck.cpp LINK_LIBS clangAST Index: clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp +++ clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp @@ -12,6 +12,7 @@ #include "../ClangTidyModuleRegistry.h" #include "ProTypeConstCastCheck.h" #include "ProTypeReinterpretCastCheck.h" +#include "ProTypeStaticCastDowncastCheck.h" namespace clang { namespace tidy { @@ -25,6 +26,8 @@ "cppcoreguidelines-pro-type-const-cast"); CheckFactories.registerCheck( "cppcoreguidelines-pro-type-reinterpret-cast"); + CheckFactories.registerCheck( + "cppcoreguidelines-pro-type-static-cast-downcast"); } }; Index: clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/ProTypeStaticCastDowncastCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/ProTypeStaticCastDowncastCheck.h +++ clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/ProTypeStaticCastDowncastCheck.h @@ -0,0 +1,33 @@ +//===--- ProTypeStaticCastDowncastCheck.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_CPPCOREGUIDELINES_PRO_TYPE_STATIC_CAST_DOWNCAST_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_STATIC_CAST_DOWNCAST_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { + +/// Checks for usages of static_cast, where a base class is downcasted to a derived class. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-type-static-cast-downcast.html +class ProTypeStaticCastDowncastCheck : public ClangTidyCheck { +public: + ProTypeStaticCastDowncastCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_STATIC_CAST_DOWNCAST_H Index: clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/ProTypeStaticCastDowncastCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/ProTypeStaticCastDowncastCheck.cpp +++ clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/ProTypeStaticCastDowncastCheck.cpp @@ -0,0 +1,52 @@ +//===--- ProTypeStaticCastDowncastCheck.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 "ProTypeStaticCastDowncastCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { + +void ProTypeStaticCastDowncastCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus) + return; + + Finder->addMatcher( + cxxStaticCastExpr(unless(isInTemplateInstantiation())).bind("cast"), + this); +} + +void ProTypeStaticCastDowncastCheck::check(const MatchFinder::MatchResult &Result) { + const auto *MatchedCast = Result.Nodes.getNodeAs("cast"); + if (MatchedCast->getCastKind() != CK_BaseToDerived) + return; + + QualType SourceType = MatchedCast->getSubExpr()->getType(); + const auto *SourceDecl = SourceType->getPointeeCXXRecordDecl(); + if (!SourceDecl) // The cast is from object to reference + SourceDecl = SourceType->getAsCXXRecordDecl(); + if (!SourceDecl) + return; + + if (SourceDecl->isPolymorphic()) + diag(MatchedCast->getOperatorLoc(), + "do not use static_cast to downcast from a base to a derived class; " + "use dynamic_cast instead") + << FixItHint::CreateReplacement(MatchedCast->getOperatorLoc(), + "dynamic_cast"); + else + diag(MatchedCast->getOperatorLoc(), + "do not use static_cast to downcast from a base to a derived class"); +} + +} // namespace tidy +} // namespace clang Index: clang-tools-extra/trunk/docs/clang-tidy/checks/cppcoreguidelines-pro-type-static-cast-downcast.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/cppcoreguidelines-pro-type-static-cast-downcast.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/cppcoreguidelines-pro-type-static-cast-downcast.rst @@ -0,0 +1,10 @@ +cppcoreguidelines-pro-type-static-cast-downcast +=============================================== + +This check flags all usages of static_cast, where a base class is casted to a derived class. +In those cases, a fixit is provided to convert the cast to a dynamic_cast. + +Use of these casts can violate type safety and cause the program to access a variable that is actually of type X to be accessed as if it were of an unrelated type Z. + +This rule is part of the "Type safety" profile of the C++ Core Guidelines, see +https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#-type2-dont-use-static_cast-downcasts-use-dynamic_cast-instead 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 @@ -6,6 +6,7 @@ cert-variadic-function-def cppcoreguidelines-pro-type-const-cast cppcoreguidelines-pro-type-reinterpret-cast + cppcoreguidelines-pro-type-static-cast-downcast google-build-explicit-make-pair google-build-namespaces google-build-using-namespace Index: clang-tools-extra/trunk/test/clang-tidy/cppcoreguidelines-pro-type-static-cast-downcast.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/cppcoreguidelines-pro-type-static-cast-downcast.cpp +++ clang-tools-extra/trunk/test/clang-tidy/cppcoreguidelines-pro-type-static-cast-downcast.cpp @@ -0,0 +1,118 @@ +// RUN: %python %S/check_clang_tidy.py %s cppcoreguidelines-pro-type-static-cast-downcast %t + +class Base { +}; + +class Derived : public Base { +}; + +class Base2 { +}; + +class MultiDerived : public Base, public Base2 { +}; + +class PolymorphicBase { +public: + virtual ~PolymorphicBase(); +}; + +class PolymorphicDerived : public PolymorphicBase { +}; + +class PolymorphicMultiDerived : public Base, public PolymorphicBase { +}; + +void pointers() { + + auto P0 = static_cast(new Base()); + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use static_cast to downcast from a base to a derived class [cppcoreguidelines-pro-type-static-cast-downcast] + + const Base* B0; + auto PC0 = static_cast(B0); + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use static_cast to downcast from a base to a derived class [cppcoreguidelines-pro-type-static-cast-downcast] + + auto P1 = static_cast(new Derived()); // OK, upcast to a public base + auto P2 = static_cast(new MultiDerived()); // OK, upcast to a public base + auto P3 = static_cast(new MultiDerived()); // OK, upcast to a public base +} + +void pointers_polymorphic() { + + auto PP0 = static_cast(new PolymorphicBase()); + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use static_cast to downcast from a base to a derived class; use dynamic_cast instead [cppcoreguidelines-pro-type-static-cast-downcast] + // CHECK-FIXES: auto PP0 = dynamic_cast(new PolymorphicBase()); + + const PolymorphicBase* B0; + auto PPC0 = static_cast(B0); + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: do not use static_cast to downcast from a base to a derived class; use dynamic_cast instead [cppcoreguidelines-pro-type-static-cast-downcast] + // CHECK-FIXES: auto PPC0 = dynamic_cast(B0); + + + auto B1 = static_cast(new PolymorphicDerived()); // OK, upcast to a public base + auto B2 = static_cast(new PolymorphicMultiDerived()); // OK, upcast to a public base + auto B3 = static_cast(new PolymorphicMultiDerived()); // OK, upcast to a public base +} + +void arrays() { + Base ArrayOfBase[10]; + auto A0 = static_cast(ArrayOfBase); + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use static_cast to downcast from a base to a derived class [cppcoreguidelines-pro-type-static-cast-downcast] +} + +void arrays_polymorphic() { + PolymorphicBase ArrayOfPolymorphicBase[10]; + auto AP0 = static_cast(ArrayOfPolymorphicBase); + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use static_cast to downcast from a base to a derived class; use dynamic_cast instead + // CHECK-FIXES: auto AP0 = dynamic_cast(ArrayOfPolymorphicBase); +} + +void references() { + Base B0; + auto R0 = static_cast(B0); + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use static_cast to downcast from a base to a derived class [cppcoreguidelines-pro-type-static-cast-downcast] + Base& RefToBase = B0; + auto R1 = static_cast(RefToBase); + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use static_cast to downcast from a base to a derived class [cppcoreguidelines-pro-type-static-cast-downcast] + + const Base& ConstRefToBase = B0; + auto RC1 = static_cast(ConstRefToBase); + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use static_cast to downcast from a base to a derived class [cppcoreguidelines-pro-type-static-cast-downcast] + + + Derived RD1; + auto R2 = static_cast(RD1); // OK, upcast to a public base +} + +void references_polymorphic() { + PolymorphicBase B0; + auto RP0 = static_cast(B0); + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use static_cast to downcast from a base to a derived class; use dynamic_cast instead + // CHECK-FIXES: auto RP0 = dynamic_cast(B0); + + PolymorphicBase& RefToPolymorphicBase = B0; + auto RP1 = static_cast(RefToPolymorphicBase); + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use static_cast to downcast from a base to a derived class; use dynamic_cast instead [cppcoreguidelines-pro-type-static-cast-downcast] + // CHECK-FIXES: auto RP1 = dynamic_cast(RefToPolymorphicBase); + + const PolymorphicBase& ConstRefToPolymorphicBase = B0; + auto RPC2 = static_cast(ConstRefToPolymorphicBase); + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: do not use static_cast to downcast from a base to a derived class; use dynamic_cast instead [cppcoreguidelines-pro-type-static-cast-downcast] + // CHECK-FIXES: auto RPC2 = dynamic_cast(ConstRefToPolymorphicBase); + + PolymorphicDerived d1; + auto RP2 = static_cast(d1); // OK, upcast to a public base +} + +template +void templ() { + auto B0 = static_cast(new D()); +} + +void templ_bad_call() { + templ(); //FIXME: this should trigger a warning +} + +void templ_good_call() { + templ(); // OK, upcast to a public base +}