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 @@ -5,6 +5,7 @@ ProBoundsArrayToPointerDecayCheck.cpp ProBoundsPointerArithmeticCheck.cpp ProTypeConstCastCheck.cpp + ProTypeCstyleCastCheck.cpp ProTypeReinterpretCastCheck.cpp ProTypeStaticCastDowncastCheck.cpp ProTypeUnionAccessCheck.cpp 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 @@ -14,6 +14,7 @@ #include "ProBoundsArrayToPointerDecayCheck.h" #include "ProBoundsPointerArithmeticCheck.h" #include "ProTypeConstCastCheck.h" +#include "ProTypeCstyleCastCheck.h" #include "ProTypeReinterpretCastCheck.h" #include "ProTypeStaticCastDowncastCheck.h" #include "ProTypeUnionAccessCheck.h" @@ -33,6 +34,8 @@ "cppcoreguidelines-pro-bounds-pointer-arithmetic"); CheckFactories.registerCheck( "cppcoreguidelines-pro-type-const-cast"); + CheckFactories.registerCheck( + "cppcoreguidelines-pro-type-cstyle-cast"); CheckFactories.registerCheck( "cppcoreguidelines-pro-type-reinterpret-cast"); CheckFactories.registerCheck( Index: clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.h +++ clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.h @@ -0,0 +1,34 @@ +//===--- ProTypeCstyleCastCheck.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_CSTYLE_CAST_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_CSTYLE_CAST_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { + +/// This check flags all use of C-style casts that perform a static_cast +/// downcast, const_cast, or reinterpret_cast. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-type-cstyle-cast.html +class ProTypeCstyleCastCheck : public ClangTidyCheck { +public: + ProTypeCstyleCastCheck(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_CSTYLE_CAST_H Index: clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.cpp +++ clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.cpp @@ -0,0 +1,107 @@ +//===--- ProTypeCstyleCastCheck.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 "ProTypeCstyleCastCheck.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 { + +static bool needsConstCast(QualType SourceType, QualType DestType) { + SourceType = SourceType.getNonReferenceType(); + DestType = DestType.getNonReferenceType(); + while (SourceType->isPointerType() && DestType->isPointerType()) { + SourceType = SourceType->getPointeeType(); + DestType = DestType->getPointeeType(); + if (SourceType.isConstQualified() && !DestType.isConstQualified()) + return true; + } + return false; +} + +void ProTypeCstyleCastCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus) + return; + + Finder->addMatcher( + cStyleCastExpr(unless(isInTemplateInstantiation())).bind("cast"), this); +} + +void ProTypeCstyleCastCheck::check(const MatchFinder::MatchResult &Result) { + const auto *MatchedCast = Result.Nodes.getNodeAs("cast"); + + if (MatchedCast->getCastKind() == CK_BitCast || + MatchedCast->getCastKind() == CK_LValueBitCast || + MatchedCast->getCastKind() == CK_IntegralToPointer || + MatchedCast->getCastKind() == CK_PointerToIntegral || + MatchedCast->getCastKind() == CK_ReinterpretMemberPointer) { + diag(MatchedCast->getLocStart(), + "do not use C-style cast to convert between unrelated types"); + return; + } + + QualType SourceType = MatchedCast->getSubExpr()->getType(); + + if (MatchedCast->getCastKind() == CK_BaseToDerived) { + const auto *SourceDecl = SourceType->getPointeeCXXRecordDecl(); + if (!SourceDecl) // The cast is from object to reference. + SourceDecl = SourceType->getAsCXXRecordDecl(); + if (!SourceDecl) + return; + + if (SourceDecl->isPolymorphic()) { + // Leave type spelling exactly as it was (unlike + // getTypeAsWritten().getAsString() which would spell enum types 'enum + // X'). + StringRef DestTypeString = Lexer::getSourceText( + CharSourceRange::getTokenRange( + MatchedCast->getLParenLoc().getLocWithOffset(1), + MatchedCast->getRParenLoc().getLocWithOffset(-1)), + *Result.SourceManager, Result.Context->getLangOpts()); + + auto diag_builder = diag( + MatchedCast->getLocStart(), + "do not use C-style cast to downcast from a base to a derived class; " + "use dynamic_cast instead"); + + const Expr *SubExpr = + MatchedCast->getSubExprAsWritten()->IgnoreImpCasts(); + std::string CastText = ("dynamic_cast<" + DestTypeString + ">").str(); + if (!isa(SubExpr)) { + CastText.push_back('('); + diag_builder << FixItHint::CreateInsertion( + Lexer::getLocForEndOfToken(SubExpr->getLocEnd(), 0, + *Result.SourceManager, + Result.Context->getLangOpts()), + ")"); + } + auto ParenRange = CharSourceRange::getTokenRange( + MatchedCast->getLParenLoc(), MatchedCast->getRParenLoc()); + diag_builder << FixItHint::CreateReplacement(ParenRange, CastText); + } else { + diag( + MatchedCast->getLocStart(), + "do not use C-style cast to downcast from a base to a derived class"); + } + return; + } + + if (MatchedCast->getCastKind() == CK_NoOp && + needsConstCast(SourceType, MatchedCast->getType())) { + diag(MatchedCast->getLocStart(), + "do not use C-style cast to cast away constness"); + } +} + +} // namespace tidy +} // namespace clang Index: clang-tools-extra/trunk/docs/clang-tidy/checks/cppcoreguidelines-pro-type-cstyle-cast.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/cppcoreguidelines-pro-type-cstyle-cast.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/cppcoreguidelines-pro-type-cstyle-cast.rst @@ -0,0 +1,15 @@ +cppcoreguidelines-pro-type-cstyle-cast +====================================== + +This check flags all use of C-style casts that perform a static_cast downcast, const_cast, or reinterpret_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. Note that a C-style (T)expression cast means to perform +the first of the following that is possible: a const_cast, a static_cast, a +static_cast followed by a const_cast, a reinterpret_cast, or a +reinterpret_cast followed by a const_cast. This rule bans (T)expression +only when used to perform an unsafe cast. + +This rule is part of the "Type safety" profile of the C++ Core Guidelines, see +https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#-type4-dont-use-c-style-texpression-casts-that-would-perform-a-static_cast-downcast-const_cast-or-reinterpret_cast. 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 @@ -7,6 +7,7 @@ cppcoreguidelines-pro-bounds-array-to-pointer-decay cppcoreguidelines-pro-bounds-pointer-arithmetic cppcoreguidelines-pro-type-const-cast + cppcoreguidelines-pro-type-cstyle-cast cppcoreguidelines-pro-type-reinterpret-cast cppcoreguidelines-pro-type-static-cast-downcast cppcoreguidelines-pro-type-union-access Index: clang-tools-extra/trunk/test/clang-tidy/cppcoreguidelines-pro-type-cstyle-cast.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/cppcoreguidelines-pro-type-cstyle-cast.cpp +++ clang-tools-extra/trunk/test/clang-tidy/cppcoreguidelines-pro-type-cstyle-cast.cpp @@ -0,0 +1,141 @@ +// RUN: %check_clang_tidy %s cppcoreguidelines-pro-type-cstyle-cast %t + +void reinterpretcast() { + int i = 0; + void *j; + j = (int*)j; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: do not use C-style cast to convert between unrelated types [cppcoreguidelines-pro-type-cstyle-cast] +} + +void constcast() { + int* i; + const int* j; + i = (int*)j; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: do not use C-style cast to cast away constness + j = (const int*)i; // OK, const added + (void)j; // OK, not a const_cast +} + +void const_and_reinterpret() { + int* i; + const void* j; + i = (int*)j; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: do not use C-style cast to convert between unrelated types +} + +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 = (Derived*)new Base(); + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use C-style cast to downcast from a base to a derived class + + const Base* B0; + auto PC0 = (const Derived*)(B0); + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use C-style cast to downcast from a base to a derived class + + auto P1 = (Base*)new Derived(); // OK, upcast to a public base + auto P2 = (Base*)new MultiDerived(); // OK, upcast to a public base + auto P3 = (Base2*)new MultiDerived(); // OK, upcast to a public base +} + +void pointers_polymorphic() { + + auto PP0 = (PolymorphicDerived*)new PolymorphicBase(); + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use C-style cast to downcast from a base to a derived class; use dynamic_cast instead + // CHECK-FIXES: auto PP0 = dynamic_cast(new PolymorphicBase()); + + const PolymorphicBase* B0; + auto PPC0 = (const PolymorphicDerived*)B0; + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: do not use C-style cast to downcast from a base to a derived class; use dynamic_cast instead + // CHECK-FIXES: auto PPC0 = dynamic_cast(B0); + + + auto B1 = (PolymorphicBase*)new PolymorphicDerived(); // OK, upcast to a public base + auto B2 = (PolymorphicBase*)new PolymorphicMultiDerived(); // OK, upcast to a public base + auto B3 = (Base*)new PolymorphicMultiDerived(); // OK, upcast to a public base +} + +void arrays() { + Base ArrayOfBase[10]; + auto A0 = (Derived*)ArrayOfBase; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use C-style cast to downcast from a base to a derived class +} + +void arrays_polymorphic() { + PolymorphicBase ArrayOfPolymorphicBase[10]; + auto AP0 = (PolymorphicDerived*)ArrayOfPolymorphicBase; + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use C-style 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 = (Derived&)B0; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use C-style cast to downcast from a base to a derived class + Base& RefToBase = B0; + auto R1 = (Derived&)RefToBase; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use C-style cast to downcast from a base to a derived class + + const Base& ConstRefToBase = B0; + auto RC1 = (const Derived&)ConstRefToBase; + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use C-style cast to downcast from a base to a derived class + + + Derived RD1; + auto R2 = (Base&)RD1; // OK, upcast to a public base +} + +void references_polymorphic() { + PolymorphicBase B0; + auto RP0 = (PolymorphicDerived&)B0; + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use C-style 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 = (PolymorphicDerived&)RefToPolymorphicBase; + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use C-style cast to downcast from a base to a derived class; use dynamic_cast instead + // CHECK-FIXES: auto RP1 = dynamic_cast(RefToPolymorphicBase); + + const PolymorphicBase& ConstRefToPolymorphicBase = B0; + auto RPC2 = (const PolymorphicDerived&)(ConstRefToPolymorphicBase); + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: do not use C-style cast to downcast from a base to a derived class; use dynamic_cast instead + // CHECK-FIXES: auto RPC2 = dynamic_cast(ConstRefToPolymorphicBase); + + PolymorphicDerived d1; + auto RP2 = (PolymorphicBase&)d1; // OK, upcast to a public base +} + +template +void templ() { + auto B0 = (B*)new D(); +} + +void templ_bad_call() { + templ(); //FIXME: this should trigger a warning +} + +void templ_good_call() { + templ(); // OK, upcast to a public base +}