Index: clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.h =================================================================== --- clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.h +++ clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.h @@ -34,6 +34,11 @@ : ClangTidyCheck(Name, Context) {} void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + template + void checkExpr(const ast_matchers::MatchFinder::MatchResult &Result, + const CastExprT *CastExpr); }; } // namespace readability Index: clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp =================================================================== --- clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp +++ clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp @@ -31,6 +31,11 @@ unless(isInTemplateInstantiation())) .bind("cast"), this); + Finder->addMatcher( + cxxFunctionalCastExpr(unless(hasDescendant(cxxConstructExpr())), + unless(hasDescendant(initListExpr()))) + .bind("functional_cast"), + this); } static bool needsConstCast(QualType SourceType, QualType DestType) { @@ -57,7 +62,18 @@ void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) { const auto *CastExpr = Result.Nodes.getNodeAs("cast"); + const auto *FunctionalCastExpr = + Result.Nodes.getNodeAs("functional_cast"); + + if (CastExpr) + checkExpr(Result, CastExpr); + else if (FunctionalCastExpr) + checkExpr(Result, FunctionalCastExpr); +} +template +void AvoidCStyleCastsCheck::checkExpr(const MatchFinder::MatchResult &Result, + const CastExprT *CastExpr) { // Ignore casts in macros. if (CastExpr->getExprLoc().isMacroID()) return; @@ -80,8 +96,14 @@ const QualType SourceType = SourceTypeAsWritten.getCanonicalType(); const QualType DestType = DestTypeAsWritten.getCanonicalType(); - auto ReplaceRange = CharSourceRange::getCharRange( - CastExpr->getLParenLoc(), CastExpr->getSubExprAsWritten()->getBeginLoc()); + CharSourceRange ReplaceRange; + if (isa(CastExpr)) + ReplaceRange = CharSourceRange::getCharRange( + CastExpr->getLParenLoc(), + CastExpr->getSubExprAsWritten()->getBeginLoc()); + else if (isa(CastExpr)) + ReplaceRange = CharSourceRange::getCharRange(CastExpr->getBeginLoc(), + CastExpr->getLParenLoc()); bool FnToFnCast = IsFunction(SourceTypeAsWritten) && IsFunction(DestTypeAsWritten); @@ -124,18 +146,27 @@ // Leave type spelling exactly as it was (unlike // getTypeAsWritten().getAsString() which would spell enum types 'enum X'). - StringRef DestTypeString = - Lexer::getSourceText(CharSourceRange::getTokenRange( - CastExpr->getLParenLoc().getLocWithOffset(1), - CastExpr->getRParenLoc().getLocWithOffset(-1)), - SM, getLangOpts()); + StringRef DestTypeString; + + if (isa(CastExpr)) + DestTypeString = + Lexer::getSourceText(CharSourceRange::getTokenRange( + CastExpr->getLParenLoc().getLocWithOffset(1), + CastExpr->getRParenLoc().getLocWithOffset(-1)), + SM, getLangOpts()); + else if (isa(CastExpr)) + DestTypeString = + Lexer::getSourceText(CharSourceRange::getTokenRange( + CastExpr->getBeginLoc(), + CastExpr->getLParenLoc().getLocWithOffset(-1)), + SM, getLangOpts()); auto Diag = diag(CastExpr->getBeginLoc(), "C-style casts are discouraged; use %0"); auto ReplaceWithCast = [&](std::string CastText) { const Expr *SubExpr = CastExpr->getSubExprAsWritten()->IgnoreImpCasts(); - if (!isa(SubExpr)) { + if (!isa(SubExpr) && !isa(CastExpr)) { CastText.push_back('('); Diag << FixItHint::CreateInsertion( Lexer::getLocForEndOfToken(SubExpr->getEndLoc(), 0, SM, @@ -222,6 +253,12 @@ Diag << "static_cast/const_cast/reinterpret_cast"; } +// Explicit instantiation +template void AvoidCStyleCastsCheck::checkExpr(const MatchFinder::MatchResult &, + const CStyleCastExpr *); +template void AvoidCStyleCastsCheck::checkExpr(const MatchFinder::MatchResult &, + const CXXFunctionalCastExpr *); + } // namespace readability } // namespace google } // namespace tidy Index: clang-tools-extra/docs/ReleaseNotes.rst =================================================================== --- clang-tools-extra/docs/ReleaseNotes.rst +++ clang-tools-extra/docs/ReleaseNotes.rst @@ -136,6 +136,8 @@ - Removed default setting `cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors = "true"`, to match the current state of the C++ Core Guidelines. +- Updated `google-readability-casting` to diagnose and fix functional casts, to achieve feature + parity with the corresponding `cpplint.py` check. Removed checks ^^^^^^^^^^^^^^ Index: clang-tools-extra/test/clang-tidy/checkers/google-readability-casting.cpp =================================================================== --- clang-tools-extra/test/clang-tidy/checkers/google-readability-casting.cpp +++ clang-tools-extra/test/clang-tidy/checkers/google-readability-casting.cpp @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy -std=c++11-or-later %s google-readability-casting %t +// RUN: %check_clang_tidy -std=c++11-or-later %s google-readability-casting %t -- -- -Wno-c++11-narrowing bool g() { return false; } @@ -143,11 +143,10 @@ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: redundant cast to the same type // CHECK-FIXES: {{^}} kZero; - int b2 = int(b); - int b3 = static_cast(b); - int b4 = b; + int b2 = static_cast(b); + int b3 = b; double aa = a; - (void)b2; + (void)aa; return (void)g(); } @@ -321,3 +320,17 @@ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: C-style casts are discouraged; use constructor call syntax [ // CHECK-FIXES: auto s6 = S(cr); } + +void functional_casts() { + float x = 1.5F; + auto y = int(x); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: C-style casts are discouraged; use static_cast + // CHECK-FIXES: auto y = static_cast(x); + + // This if fine, compiler won't allow implicit conversions with brace initialization + auto z = int{x}; + + // Functional casts are allowed if S is of class type + const char *str = "foo"; + auto s = S(str); +}