Index: clang-tidy/abseil/AbseilTidyModule.cpp =================================================================== --- clang-tidy/abseil/AbseilTidyModule.cpp +++ clang-tidy/abseil/AbseilTidyModule.cpp @@ -18,6 +18,7 @@ #include "RedundantStrcatCallsCheck.h" #include "StringFindStartswithCheck.h" #include "StrCatAppendCheck.h" +#include "UpgradeDurationConversionsCheck.h" namespace clang { namespace tidy { @@ -41,6 +42,8 @@ "abseil-str-cat-append"); CheckFactories.registerCheck( "abseil-string-find-startswith"); + CheckFactories.registerCheck( + "abseil-upgrade-duration-conversions"); } }; Index: clang-tidy/abseil/CMakeLists.txt =================================================================== --- clang-tidy/abseil/CMakeLists.txt +++ clang-tidy/abseil/CMakeLists.txt @@ -10,6 +10,7 @@ RedundantStrcatCallsCheck.cpp StrCatAppendCheck.cpp StringFindStartswithCheck.cpp + UpgradeDurationConversionsCheck.cpp LINK_LIBS clangAST Index: clang-tidy/abseil/UpgradeDurationConversionsCheck.h =================================================================== --- clang-tidy/abseil/UpgradeDurationConversionsCheck.h +++ clang-tidy/abseil/UpgradeDurationConversionsCheck.h @@ -0,0 +1,35 @@ +//===--- UpgradeDurationConversionsCheck.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_ABSEIL_UPGRADEDURATIONCONVERSIONSCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_UPGRADEDURATIONCONVERSIONSCHECK_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace abseil { + +/// Finds deprecated uses of absl::Duration arithmetic operators and factories. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/abseil-upgrade-duration-conversions.html +class UpgradeDurationConversionsCheck : public ClangTidyCheck { +public: + UpgradeDurationConversionsCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace abseil +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_UPGRADEDURATIONCONVERSIONSCHECK_H Index: clang-tidy/abseil/UpgradeDurationConversionsCheck.cpp =================================================================== --- clang-tidy/abseil/UpgradeDurationConversionsCheck.cpp +++ clang-tidy/abseil/UpgradeDurationConversionsCheck.cpp @@ -0,0 +1,172 @@ +//===--- UpgradeDurationConversionsCheck.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 "UpgradeDurationConversionsCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace abseil { + +void UpgradeDurationConversionsCheck::registerMatchers( + ast_matchers::MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus) + return; + + // For the arithmetic calls, we match only the uses of the templated operators + // where the template parameter is not a built-in type. This means the + // instantiation makes use of an available user defined conversion to int64_t. + // + // The implementation of these templates will be updated to fail SFINAE for + // non-integral types. We match them to suggest an explicit cast. + + // a *= b; a /= b; + Finder->addMatcher( + cxxOperatorCallExpr( + argumentCountIs(2), + hasArgument( + 0, expr(hasType(cxxRecordDecl(hasName("::absl::Duration"))))), + hasArgument(1, expr().bind("arg")), + callee(functionDecl( + hasParent(functionTemplateDecl()), + unless(hasTemplateArgument(0, refersToType(builtinType()))), + anyOf(hasName("operator*="), hasName("operator/="))))) + .bind("call"), + this); + + // a.operator*=(b); a.operator/=(b); + Finder->addMatcher( + cxxMemberCallExpr( + callee(cxxMethodDecl( + ofClass(cxxRecordDecl(hasName("::absl::Duration"))), + hasParent(functionTemplateDecl()), + unless(hasTemplateArgument(0, refersToType(builtinType()))), + anyOf(hasName("operator*="), hasName("operator/=")))), + argumentCountIs(1), hasArgument(0, expr().bind("arg"))) + .bind("call"), + this); + + // a * b; a / b; operator*(a, b); operator/(a, b); where T is second arg + Finder->addMatcher( + callExpr(callee(functionDecl( + hasParent(functionTemplateDecl()), + unless(hasTemplateArgument(0, refersToType(builtinType()))), + anyOf(hasName("::absl::operator*"), + hasName("::absl::operator/")))), + argumentCountIs(2), + hasArgument(0, expr(hasType( + cxxRecordDecl(hasName("::absl::Duration"))))), + hasArgument(1, expr().bind("arg"))) + .bind("call"), + this); + + // a * b; operator*(a, b); where T is first arg + Finder->addMatcher( + callExpr(callee(functionDecl( + hasParent(functionTemplateDecl()), + unless(hasTemplateArgument(0, refersToType(builtinType()))), + hasName("::absl::operator*"))), + argumentCountIs(2), hasArgument(0, expr().bind("arg")), + hasArgument(1, expr(hasType( + cxxRecordDecl(hasName("::absl::Duration")))))) + .bind("call"), + this); + + // For the factory functions, we match only the non-templated overloads that + // take an int64_t parameter. Within these calls, we care about implicit + // casts through a user defined conversions to int64_t. + // + // The factory functions will be updated to be templated and SFINAE on whether + // the template parameter is an integral type. This complements the already + // existing templated overloads that only accept floating point types. + + // absl::Nanoseconds() + // absl::Microseconds() + // absl::Milliseconds() + // absl::Seconds() + // absl::Minutes() + // absl::Hours() + Finder->addMatcher( + implicitCastExpr( + anyOf(hasCastKind(CK_UserDefinedConversion), + has(implicitCastExpr(hasCastKind(CK_UserDefinedConversion)))), + hasParent(callExpr(callee(functionDecl( + anyOf(hasName("::absl::Nanoseconds"), + hasName("::absl::Microseconds"), + hasName("::absl::Milliseconds"), + hasName("::absl::Seconds"), + hasName("::absl::Minutes"), + hasName("::absl::Hours")), + unless(hasParent(functionTemplateDecl())))), + hasArgument(0, expr().bind("arg"))) + .bind("call"))), + this); +} + +namespace { + +AST_MATCHER_P(Expr, hasSourceRange, SourceRange, Range) { + return Node.getSourceRange() == Range; +} + +AST_MATCHER(Expr, isInstantiationOfDependentExpr) { + auto HasMatchingDependentDescendant = hasDescendant( + expr(hasSourceRange(Node.getSourceRange()), isInstantiationDependent())); + return expr(anyOf(hasAncestor( + functionTemplateDecl(HasMatchingDependentDescendant)), + hasAncestor( + classTemplateDecl(HasMatchingDependentDescendant)))) + .matches(Node, Finder, Builder); +} + +} // namespace + +void UpgradeDurationConversionsCheck::check( + const ast_matchers::MatchFinder::MatchResult &Result) { + const auto *ArgExpr = Result.Nodes.getNodeAs("arg"); + llvm::StringRef Message = "perform explicit cast on expression getting " + "implicitly converted to int64_t"; + + CharSourceRange SourceRange = Lexer::makeFileCharRange( + CharSourceRange::getTokenRange(ArgExpr->getSourceRange()), + *Result.SourceManager, Result.Context->getLangOpts()); + if (SourceRange.isInvalid()) { + // The invalid source range likely means we are inside a macro body. Warn + // but do not try to suggest a fix. + diag(ArgExpr->getBeginLoc(), Message); + return; + } + + // If we are inside a template instantiation, we warn if the call expression + // in the original template is dependent on the instantiation, (i.e. depends + // on template parameters). This means the expression in the template does not + // trigger without the instantiation. Do not suggest a fix in this case, since + // a manual fix is likely needed. + if (!match(isInTemplateInstantiation(), *ArgExpr, *Result.Context).empty()) { + const auto *CallExpr = Result.Nodes.getNodeAs("call"); + if (!match(expr(isInstantiationOfDependentExpr()), *CallExpr, + *Result.Context) + .empty()) { + diag(ArgExpr->getBeginLoc(), Message); + } + return; + } + + FixItHint Fixes[] = {FixItHint::CreateInsertion(SourceRange.getBegin(), + "static_cast("), + FixItHint::CreateInsertion(SourceRange.getEnd(), ")")}; + diag(ArgExpr->getBeginLoc(), Message) << Fixes; +} + +} // namespace abseil +} // namespace tidy +} // namespace clang Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -110,6 +110,13 @@ Flags uses of ``absl::StrCat()`` to append to a ``std::string``. Suggests ``absl::StrAppend()`` should be used instead. +- New :doc:`abseil-upgrade-duration-conversions + ` check. + + Finds calls to ``absl::Duration`` arithmetic operators and factories whose + argument needs an explicit cast to continue compiling after upcoming API + changes. + - New :doc:`cppcoreguidelines-macro-usage ` check. Index: docs/clang-tidy/checks/abseil-upgrade-duration-conversions.rst =================================================================== --- docs/clang-tidy/checks/abseil-upgrade-duration-conversions.rst +++ docs/clang-tidy/checks/abseil-upgrade-duration-conversions.rst @@ -0,0 +1,43 @@ +.. title:: clang-tidy - abseil-upgrade-duration-conversions + +abseil-upgrade-duration-conversions +=================================== + +Finds calls to ``absl::Duration`` arithmetic operators and factories whose +argument needs an explicit cast to continue compiling after upcoming API +changes. + +The operators ``*=``, ``/=``, ``*``, and ``/`` for ``absl::Duration`` currently +accept an argument of class type that is convertible to an arithmetic type. Such +a call currently converts the value to an ``int64_t``, even in a case such as +``std::atomic`` that would result in lossy conversion. + +Additionally, the ``absl::Duration`` factory functions (``absl::Hours``, +``absl::Minutes``, etc) currently accept an ``int64_t`` or a floating point +type. Similar to the arithmetic operators, calls with an argument of class type +that is convertible to an arithmetic type go through the ``int64_t`` path. + +The operators and factories will be updated to only accept arithmetic types to +prevent unintended behavior. Passing an argument of class type will result in +compile error, even if the type is implicitly convertible to an arithmetic type. + +Here are example fixes created by this check: + +.. code-block:: c++ + + std::atomic a; + absl::Duration d = absl::Milliseconds(a); + d *= a; + +becomes + +.. code-block:: c++ + + std::atomic a; + absl::Duration d = absl::Milliseconds(static_cast(a)); + d *= static_cast(a); + +Note that this check always adds a cast to ``int64_t`` in order to preserve the +current behavior of user code. It is possible that this uncovers unintended +behavior due to types implicitly convertible to a floating point type. + Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -10,8 +10,9 @@ abseil-no-internal-dependencies abseil-no-namespace abseil-redundant-strcat-calls + abseil-str-cat-append abseil-string-find-startswith - abseil-str-cat-append + abseil-upgrade-duration-conversions android-cloexec-accept android-cloexec-accept4 android-cloexec-creat @@ -151,6 +152,7 @@ hicpp-special-member-functions (redirects to cppcoreguidelines-special-member-functions) hicpp-static-assert (redirects to misc-static-assert) hicpp-undelegated-constructor (redirects to bugprone-undelegated-constructor) + hicpp-uppercase-literal-suffix (redirects to readability-uppercase-literal-suffix) hicpp-use-auto (redirects to modernize-use-auto) hicpp-use-emplace (redirects to modernize-use-emplace) hicpp-use-equals-default (redirects to modernize-use-equals-default) @@ -159,7 +161,6 @@ hicpp-use-nullptr (redirects to modernize-use-nullptr) hicpp-use-override (redirects to modernize-use-override) hicpp-vararg (redirects to cppcoreguidelines-pro-type-vararg) - hicpp-uppercase-literal-suffix (redirects to readability-uppercase-literal-suffix) llvm-header-guard llvm-include-order llvm-namespace-comment Index: test/clang-tidy/abseil-upgrade-duration-conversions.cpp =================================================================== --- test/clang-tidy/abseil-upgrade-duration-conversions.cpp +++ test/clang-tidy/abseil-upgrade-duration-conversions.cpp @@ -0,0 +1,357 @@ +// RUN: %check_clang_tidy %s abseil-upgrade-duration-conversions %t + +using int64_t = long long; + +// Partial implementation of relevant APIs from +// https://github.com/abseil/abseil-cpp/blob/master/absl/time/time.h +namespace absl { + +class Duration { +public: + Duration &operator*=(int64_t r); + Duration &operator*=(float r); + Duration &operator*=(double r); + template Duration &operator*=(T r); + + Duration &operator/=(int64_t r); + Duration &operator/=(float r); + Duration &operator/=(double r); + template Duration &operator/=(T r); +}; + +template Duration operator*(Duration lhs, T rhs); +template Duration operator*(T lhs, Duration rhs); +template Duration operator/(Duration lhs, T rhs); + +constexpr Duration Nanoseconds(int64_t n); +constexpr Duration Microseconds(int64_t n); +constexpr Duration Milliseconds(int64_t n); +constexpr Duration Seconds(int64_t n); +constexpr Duration Minutes(int64_t n); +constexpr Duration Hours(int64_t n); + +template struct EnableIfFloatImpl {}; +template <> struct EnableIfFloatImpl { typedef int Type; }; +template <> struct EnableIfFloatImpl { typedef int Type; }; +template <> struct EnableIfFloatImpl { typedef int Type; }; +template using EnableIfFloat = typename EnableIfFloatImpl::Type; + +template = 0> Duration Nanoseconds(T n); +template = 0> Duration Microseconds(T n); +template = 0> Duration Milliseconds(T n); +template = 0> Duration Seconds(T n); +template = 0> Duration Minutes(T n); +template = 0> Duration Hours(T n); + +} // namespace absl + +template struct ConvertibleTo { + operator T() const; +}; + +void arithmeticOperatorBasicPositive() { + absl::Duration d; + d *= ConvertibleTo(); + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: d *= static_cast(ConvertibleTo()); + d /= ConvertibleTo(); + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: d /= static_cast(ConvertibleTo()); + d = ConvertibleTo() * d; + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: d = static_cast(ConvertibleTo()) * d; + d = d * ConvertibleTo(); + // CHECK-MESSAGES: [[@LINE-1]]:11: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: d = d * static_cast(ConvertibleTo()); + d = d / ConvertibleTo(); + // CHECK-MESSAGES: [[@LINE-1]]:11: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: d = d / static_cast(ConvertibleTo()); + d.operator*=(ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:16: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: d.operator*=(static_cast(ConvertibleTo())); + d.operator/=(ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:16: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: d.operator/=(static_cast(ConvertibleTo())); + d = operator*(ConvertibleTo(), d); + // CHECK-MESSAGES: [[@LINE-1]]:17: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: d = operator*(static_cast(ConvertibleTo()), d); + d = operator*(d, ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:20: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: d = operator*(d, static_cast(ConvertibleTo())); + d = operator/(d, ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:20: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: d = operator/(d, static_cast(ConvertibleTo())); +} + +void arithmeticOperatorBasicNegative() { + absl::Duration d; + d *= char{1}; + d *= 1; + d *= int64_t{1}; + d *= 1.0f; + d *= 1.0; + d *= 1.0l; + d /= char{1}; + d /= 1; + d /= int64_t{1}; + d /= 1.0f; + d /= 1.0; + d /= 1.0l; + d = d * char{1}; + d = d * 1; + d = d * int64_t{1}; + d = d * 1.0f; + d = d * 1.0; + d = d * 1.0l; + d = char{1} * d; + d = 1 * d; + d = int64_t{1} * d; + d = 1.0f * d; + d = 1.0 * d; + d = 1.0l * d; + d = d / char{1}; + d = d / 1; + d = d / int64_t{1}; + d = d / 1.0f; + d = d / 1.0; + d = d / 1.0l; + + d *= static_cast(ConvertibleTo()); + d *= (int)ConvertibleTo(); + d *= int(ConvertibleTo()); + d /= static_cast(ConvertibleTo()); + d /= (int)ConvertibleTo(); + d /= int(ConvertibleTo()); + d = static_cast(ConvertibleTo()) * d; + d = (int)ConvertibleTo() * d; + d = int(ConvertibleTo()) * d; + d = d * static_cast(ConvertibleTo()); + d = d * (int)ConvertibleTo(); + d = d * int(ConvertibleTo()); + d = d / static_cast(ConvertibleTo()); + d = d / (int)ConvertibleTo(); + d = d / int(ConvertibleTo()); + + d *= 1 + ConvertibleTo(); + d /= 1 + ConvertibleTo(); + d = (1 + ConvertibleTo()) * d; + d = d * (1 + ConvertibleTo()); + d = d / (1 + ConvertibleTo()); +} + +template void templateForOpsSpecialization(T) {} +template <> +void templateForOpsSpecialization(absl::Duration d) { + d *= ConvertibleTo(); + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: d *= static_cast(ConvertibleTo()); +} + +template void templateOpsFix() { + absl::Duration d; + d *= ConvertibleTo(); + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: d *= static_cast(ConvertibleTo()); +} + +template void templateOpsWarnOnly(T t, U u) { + t *= u; + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: perform explicit cast on expression getting implicitly converted to int64_t + absl::Duration d; + d *= u; + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: perform explicit cast on expression getting implicitly converted to int64_t +} + +template struct TemplateTypeOpsWarnOnly { + void memberA(T t) { + d *= t; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: perform explicit cast on expression getting implicitly converted to int64_t + } + template void memberB(U u, V v) { + u *= v; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: perform explicit cast on expression getting implicitly converted to int64_t + d *= v; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: perform explicit cast on expression getting implicitly converted to int64_t + } + + absl::Duration d; +}; + +void arithmeticOperatorsInTemplates() { + templateForOpsSpecialization(5); + templateForOpsSpecialization(absl::Duration()); + templateOpsFix(); + templateOpsWarnOnly(absl::Duration(), ConvertibleTo()); + TemplateTypeOpsWarnOnly> t; + t.memberA(ConvertibleTo()); + t.memberB(absl::Duration(), ConvertibleTo()); +} + +#define FUNCTION_MACRO(x) x +#define CONVERTIBLE_TMP ConvertibleTo() +#define ONLY_WARN_INSIDE_MACRO_ARITHMETIC_OP d *= ConvertibleTo() + +void arithmeticOperatorsInMacros() { + absl::Duration d; + d = FUNCTION_MACRO(d * ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:26: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: d = FUNCTION_MACRO(d * static_cast(ConvertibleTo())); + d *= FUNCTION_MACRO(ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:23: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: d *= static_cast(FUNCTION_MACRO(ConvertibleTo())); + d *= CONVERTIBLE_TMP; + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: d *= static_cast(CONVERTIBLE_TMP); + ONLY_WARN_INSIDE_MACRO_ARITHMETIC_OP; + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: perform explicit cast on expression getting implicitly converted to int64_t +} + +void factoryFunctionPositive() { + // User defined conversion: + (void)absl::Nanoseconds(ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:27: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: (void)absl::Nanoseconds(static_cast(ConvertibleTo())); + (void)absl::Microseconds(ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:28: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: (void)absl::Microseconds(static_cast(ConvertibleTo())); + (void)absl::Milliseconds(ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:28: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: (void)absl::Milliseconds(static_cast(ConvertibleTo())); + (void)absl::Seconds(ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:23: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: (void)absl::Seconds(static_cast(ConvertibleTo())); + (void)absl::Minutes(ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:23: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: (void)absl::Minutes(static_cast(ConvertibleTo())); + (void)absl::Hours(ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:21: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: (void)absl::Hours(static_cast(ConvertibleTo())); + + // User defined conversion to integral type, followed by built-in conversion: + (void)absl::Nanoseconds(ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:27: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: (void)absl::Nanoseconds(static_cast(ConvertibleTo())); + (void)absl::Microseconds(ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:28: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: (void)absl::Microseconds(static_cast(ConvertibleTo())); + (void)absl::Milliseconds(ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:28: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: (void)absl::Milliseconds(static_cast(ConvertibleTo())); + (void)absl::Seconds(ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:23: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: (void)absl::Seconds(static_cast(ConvertibleTo())); + (void)absl::Minutes(ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:23: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: (void)absl::Minutes(static_cast(ConvertibleTo())); + (void)absl::Hours(ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:21: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: (void)absl::Hours(static_cast(ConvertibleTo())); + + // User defined conversion to floating point type, followed by built-in conversion: + (void)absl::Nanoseconds(ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:27: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: (void)absl::Nanoseconds(static_cast(ConvertibleTo())); + (void)absl::Microseconds(ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:28: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: (void)absl::Microseconds(static_cast(ConvertibleTo())); + (void)absl::Milliseconds(ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:28: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: (void)absl::Milliseconds(static_cast(ConvertibleTo())); + (void)absl::Seconds(ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:23: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: (void)absl::Seconds(static_cast(ConvertibleTo())); + (void)absl::Minutes(ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:23: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: (void)absl::Minutes(static_cast(ConvertibleTo())); + (void)absl::Hours(ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:21: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: (void)absl::Hours(static_cast(ConvertibleTo())); +} + +void factoryFunctionNegative() { + (void)absl::Nanoseconds(char{1}); + (void)absl::Nanoseconds(1); + (void)absl::Nanoseconds(int64_t{1}); + (void)absl::Nanoseconds(1.0f); + (void)absl::Microseconds(char{1}); + (void)absl::Microseconds(1); + (void)absl::Microseconds(int64_t{1}); + (void)absl::Microseconds(1.0f); + (void)absl::Milliseconds(char{1}); + (void)absl::Milliseconds(1); + (void)absl::Milliseconds(int64_t{1}); + (void)absl::Milliseconds(1.0f); + (void)absl::Seconds(char{1}); + (void)absl::Seconds(1); + (void)absl::Seconds(int64_t{1}); + (void)absl::Seconds(1.0f); + (void)absl::Minutes(char{1}); + (void)absl::Minutes(1); + (void)absl::Minutes(int64_t{1}); + (void)absl::Minutes(1.0f); + (void)absl::Hours(char{1}); + (void)absl::Hours(1); + (void)absl::Hours(int64_t{1}); + (void)absl::Hours(1.0f); + + (void)absl::Nanoseconds(static_cast(ConvertibleTo())); + (void)absl::Microseconds(static_cast(ConvertibleTo())); + (void)absl::Milliseconds(static_cast(ConvertibleTo())); + (void)absl::Seconds(static_cast(ConvertibleTo())); + (void)absl::Minutes(static_cast(ConvertibleTo())); + (void)absl::Hours(static_cast(ConvertibleTo())); +} + +template void templateForFactorySpecialization(T) {} +template <> void templateForFactorySpecialization>(ConvertibleTo c) { + (void)absl::Nanoseconds(c); + // CHECK-MESSAGES: [[@LINE-1]]:27: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: (void)absl::Nanoseconds(static_cast(c)); +} + +template void templateFactoryFix() { + (void)absl::Nanoseconds(ConvertibleTo()); + // CHECK-MESSAGES: [[@LINE-1]]:27: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: (void)absl::Nanoseconds(static_cast(ConvertibleTo())); +} + +template void templateFactoryWarnOnly(T t) { + (void)absl::Nanoseconds(t); + // CHECK-MESSAGES: [[@LINE-1]]:27: warning: perform explicit cast on expression getting implicitly converted to int64_t +} + +template struct TemplateTypeFactoryWarnOnly { + void memberA(T t) { + (void)absl::Nanoseconds(t); + // CHECK-MESSAGES: [[@LINE-1]]:29: warning: perform explicit cast on expression getting implicitly converted to int64_t + } + template void memberB(U u) { + (void)absl::Nanoseconds(u); + // CHECK-MESSAGES: [[@LINE-1]]:29: warning: perform explicit cast on expression getting implicitly converted to int64_t + } +}; + +void factoryInTemplates() { + templateForFactorySpecialization(5); + templateForFactorySpecialization(ConvertibleTo()); + templateFactoryFix(); + templateFactoryWarnOnly(ConvertibleTo()); + TemplateTypeFactoryWarnOnly> t; + t.memberA(ConvertibleTo()); + t.memberB(ConvertibleTo()); +} + +#define ONLY_WARN_INSIDE_MACRO_FACTORY \ + (void)absl::Nanoseconds(ConvertibleTo()) + +void factoryInMacros() { + (void)absl::Nanoseconds(FUNCTION_MACRO(ConvertibleTo())); + // CHECK-MESSAGES: [[@LINE-1]]:42: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: (void)absl::Nanoseconds(static_cast(FUNCTION_MACRO(ConvertibleTo()))); + (void)absl::Nanoseconds(CONVERTIBLE_TMP); + // CHECK-MESSAGES: [[@LINE-1]]:27: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: (void)absl::Nanoseconds(static_cast(CONVERTIBLE_TMP)) + ONLY_WARN_INSIDE_MACRO_FACTORY; + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: perform explicit cast on expression getting implicitly converted to int64_t +} +