Author: alexfh Date: Sat Feb 18 03:45:00 2017 New Revision: 295544 URL: http://llvm.org/viewvc/llvm-project?rev=295544&view=rev Log: [clang-tidy] google-readability-casting: Handle user-defined conversions
Modified: clang-tools-extra/trunk/clang-tidy/google/AvoidCStyleCastsCheck.cpp clang-tools-extra/trunk/test/clang-tidy/google-readability-casting.cpp Modified: clang-tools-extra/trunk/clang-tidy/google/AvoidCStyleCastsCheck.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/google/AvoidCStyleCastsCheck.cpp?rev=295544&r1=295543&r2=295544&view=diff ============================================================================== --- clang-tools-extra/trunk/clang-tidy/google/AvoidCStyleCastsCheck.cpp (original) +++ clang-tools-extra/trunk/clang-tidy/google/AvoidCStyleCastsCheck.cpp Sat Feb 18 03:45:00 2017 @@ -35,25 +35,28 @@ void AvoidCStyleCastsCheck::registerMatc } 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; + for (;;) { + if (SourceType.isConstQualified() && !DestType.isConstQualified()) { + return (SourceType->isPointerType() == DestType->isPointerType()) && + (SourceType->isReferenceType() == DestType->isReferenceType()); + } + if ((SourceType->isPointerType() && DestType->isPointerType()) || + (SourceType->isReferenceType() && DestType->isReferenceType())) { + SourceType = SourceType->getPointeeType(); + DestType = DestType->getPointeeType(); + } else { + return false; + } } - return false; } -static bool pointedTypesAreEqual(QualType SourceType, QualType DestType) { - SourceType = SourceType.getNonReferenceType(); - DestType = DestType.getNonReferenceType(); - while (SourceType->isPointerType() && DestType->isPointerType()) { - SourceType = SourceType->getPointeeType(); - DestType = DestType->getPointeeType(); +static bool pointedUnqualifiedTypesAreEqual(QualType T1, QualType T2) { + while ((T1->isPointerType() && T2->isPointerType()) || + (T1->isReferenceType() && T2->isReferenceType())) { + T1 = T1->getPointeeType(); + T2 = T2->getPointeeType(); } - return SourceType.getUnqualifiedType() == DestType.getUnqualifiedType(); + return T1.getUnqualifiedType() == T2.getUnqualifiedType(); } void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) { @@ -67,35 +70,38 @@ void AvoidCStyleCastsCheck::check(const // Casting to void is an idiomatic way to mute "unused variable" and similar // warnings. - if (CastExpr->getTypeAsWritten()->isVoidType()) + if (CastExpr->getCastKind() == CK_ToVoid) return; - QualType SourceType = CastExpr->getSubExprAsWritten()->getType(); - QualType DestType = CastExpr->getTypeAsWritten(); - auto isFunction = [](QualType T) { T = T.getCanonicalType().getNonReferenceType(); return T->isFunctionType() || T->isFunctionPointerType() || T->isMemberFunctionPointerType(); }; - bool FnToFnCast = isFunction(SourceType) && isFunction(DestType); - - // Function pointer/reference casts may be needed to resolve ambiguities in - // case of overloaded functions, so detection of redundant casts is trickier - // in this case. Don't emit "redundant cast" warnings for function - // pointer/reference types. - if (SourceType == DestType && !FnToFnCast) { - diag(CastExpr->getLocStart(), "redundant cast to the same type") - << FixItHint::CreateRemoval(ParenRange); - return; - } - SourceType = SourceType.getCanonicalType(); - DestType = DestType.getCanonicalType(); - if (SourceType == DestType && !FnToFnCast) { - diag(CastExpr->getLocStart(), - "possibly redundant cast between typedefs of the same type"); - return; + const QualType DestTypeAsWritten = CastExpr->getTypeAsWritten(); + const QualType SourceTypeAsWritten = CastExpr->getSubExprAsWritten()->getType(); + const QualType SourceType = SourceTypeAsWritten.getCanonicalType(); + const QualType DestType = DestTypeAsWritten.getCanonicalType(); + + bool FnToFnCast = + isFunction(SourceTypeAsWritten) && isFunction(DestTypeAsWritten); + + if (CastExpr->getCastKind() == CK_NoOp && !FnToFnCast) { + // Function pointer/reference casts may be needed to resolve ambiguities in + // case of overloaded functions, so detection of redundant casts is trickier + // in this case. Don't emit "redundant cast" warnings for function + // pointer/reference types. + if (SourceTypeAsWritten == DestTypeAsWritten) { + diag(CastExpr->getLocStart(), "redundant cast to the same type") + << FixItHint::CreateRemoval(ParenRange); + return; + } + if (SourceType == DestType) { + diag(CastExpr->getLocStart(), + "possibly redundant cast between typedefs of the same type"); + return; + } } // The rest of this check is only relevant to C++. @@ -126,11 +132,8 @@ void AvoidCStyleCastsCheck::check(const auto Diag = diag(CastExpr->getLocStart(), "C-style casts are discouraged; use %0"); - auto ReplaceWithCast = [&](StringRef CastType) { - Diag << CastType; - + auto ReplaceWithCast = [&](std::string CastText) { const Expr *SubExpr = CastExpr->getSubExprAsWritten()->IgnoreImpCasts(); - std::string CastText = (CastType + "<" + DestTypeString + ">").str(); if (!isa<ParenExpr>(SubExpr)) { CastText.push_back('('); Diag << FixItHint::CreateInsertion( @@ -140,28 +143,52 @@ void AvoidCStyleCastsCheck::check(const } Diag << FixItHint::CreateReplacement(ParenRange, CastText); }; + auto ReplaceWithNamedCast = [&](StringRef CastType) { + Diag << CastType; + std::string CastText = (CastType + "<" + DestTypeString + ">").str(); + ReplaceWithCast(std::move(CastText)); + }; + auto ReplaceWithCtorCall = [&]() { + std::string CastText; + if (!DestTypeAsWritten.hasQualifiers() && + DestTypeAsWritten->isRecordType() && + !DestTypeAsWritten->isElaboratedTypeSpecifier()) { + Diag << "constructor call syntax"; + CastText = DestTypeString.str(); // FIXME: Validate DestTypeString, maybe. + } else { + Diag << "static_cast"; + CastText = ("static_cast<" + DestTypeString + ">").str(); + } + ReplaceWithCast(std::move(CastText)); + }; // Suggest appropriate C++ cast. See [expr.cast] for cast notation semantics. switch (CastExpr->getCastKind()) { case CK_FunctionToPointerDecay: - ReplaceWithCast("static_cast"); + ReplaceWithNamedCast("static_cast"); + return; + case CK_ConstructorConversion: + ReplaceWithCtorCall(); return; case CK_NoOp: if (FnToFnCast) { - ReplaceWithCast("static_cast"); + ReplaceWithNamedCast("static_cast"); return; } if (needsConstCast(SourceType, DestType) && - pointedTypesAreEqual(SourceType, DestType)) { - ReplaceWithCast("const_cast"); + pointedUnqualifiedTypesAreEqual(SourceType, DestType)) { + ReplaceWithNamedCast("const_cast"); return; } - if (DestType->isReferenceType() && - (SourceType.getNonReferenceType() == - DestType.getNonReferenceType().withConst() || - SourceType.getNonReferenceType() == DestType.getNonReferenceType())) { - ReplaceWithCast("const_cast"); - return; + if (DestType->isReferenceType()) { + QualType Dest = DestType.getNonReferenceType(); + QualType Source = SourceType.getNonReferenceType(); + if (Source == Dest.withConst() || + SourceType.getNonReferenceType() == DestType.getNonReferenceType()) { + ReplaceWithNamedCast("const_cast"); + return; + } + break; } // FALLTHROUGH case clang::CK_IntegralCast: @@ -170,14 +197,14 @@ void AvoidCStyleCastsCheck::check(const // still retained. if ((SourceType->isBuiltinType() || SourceType->isEnumeralType()) && (DestType->isBuiltinType() || DestType->isEnumeralType())) { - ReplaceWithCast("static_cast"); + ReplaceWithNamedCast("static_cast"); return; } break; case CK_BitCast: // FIXME: Suggest const_cast<...>(reinterpret_cast<...>(...)) replacement. if (!needsConstCast(SourceType, DestType)) { - ReplaceWithCast("reinterpret_cast"); + ReplaceWithNamedCast("reinterpret_cast"); return; } break; Modified: clang-tools-extra/trunk/test/clang-tidy/google-readability-casting.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/google-readability-casting.cpp?rev=295544&r1=295543&r2=295544&view=diff ============================================================================== --- clang-tools-extra/trunk/test/clang-tidy/google-readability-casting.cpp (original) +++ clang-tools-extra/trunk/test/clang-tidy/google-readability-casting.cpp Sat Feb 18 03:45:00 2017 @@ -222,3 +222,43 @@ void function_casts() { FnPtrVoid correct2 = static_cast<void (*)()>(&overloaded_function); FnRefInt correct3 = static_cast<void (&)(int)>(overloaded_function); } + +struct S { + S(const char *); +}; +struct ConvertibleToS { + operator S() const; +}; +struct ConvertibleToSRef { + operator const S&() const; +}; + +void conversions() { + //auto s1 = (const S&)""; + // C HECK-MESSAGES: :[[@LINE-1]]:10: warning: C-style casts are discouraged; use static_cast [ + // C HECK-FIXES: S s1 = static_cast<const S&>(""); + auto s2 = (S)""; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: C-style casts are discouraged; use constructor call syntax [ + // CHECK-FIXES: auto s2 = S(""); + auto s2a = (struct S)""; + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: C-style casts are discouraged; use static_cast [ + // CHECK-FIXES: auto s2a = static_cast<struct S>(""); + ConvertibleToS c; + auto s3 = (const S&)c; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: C-style casts are discouraged; use static_cast/const_cast/reinterpret_cast [ + // CHECK-FIXES: auto s3 = (const S&)c; + // FIXME: This should be a static_cast + // C HECK-FIXES: auto s3 = static_cast<const S&>(c); + auto s4 = (S)c; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: C-style casts are discouraged; use constructor call syntax [ + // CHECK-FIXES: auto s4 = S(c); + ConvertibleToSRef cr; + auto s5 = (const S&)cr; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: C-style casts are discouraged; use static_cast/const_cast/reinterpret_cast [ + // CHECK-FIXES: auto s5 = (const S&)cr; + // FIXME: This should be a static_cast + // C HECK-FIXES: auto s5 = static_cast<const S&>(cr); + auto s6 = (S)cr; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: C-style casts are discouraged; use constructor call syntax [ + // CHECK-FIXES: auto s6 = S(cr); +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits