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,177 @@ +//===--- 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 { + +namespace { + +AST_MATCHER_P(Expr, hasValidSourceRange, SourceRange, Range) { + return Node.getSourceRange().isValid() && + Node.getSourceRange() == Range; +} + +AST_MATCHER_P(Expr, isInstantiationOfDependentExpr, + ast_matchers::internal::Matcher, InnerMatcher) { + auto HasMatchingDependentDescendant = hasDescendant(expr( + hasValidSourceRange(Node.getSourceRange()), isInstantiationDependent())); + return Node.getSourceRange().isValid() && + expr(isInTemplateInstantiation(), + anyOf(hasAncestor( + functionTemplateDecl(HasMatchingDependentDescendant)), + hasAncestor( + classTemplateDecl(HasMatchingDependentDescendant)))) + .matches(Node, Finder, Builder) && + InnerMatcher.matches(Node, Finder, Builder); +} + +} // namespace + +void UpgradeDurationConversionsCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus) + return; + + auto TemplateInstantiationFilter = + anyOf(unless(isInTemplateInstantiation()), + isInstantiationOfDependentExpr(expr().bind("instantiated-expr"))); + + // 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. + + // Match expressions like `a *= b` and `a /= b` where `a` has type + // `absl::Duration` and `b` is not of a built-in type. + 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/=")))), + TemplateInstantiationFilter), + this); + + // Match expressions like `a.operator*=(b)` and `a.operator/=(b)` where `a` + // has type `absl::Duration` and `b` is not of a built-in type. + 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")), + TemplateInstantiationFilter), + this); + + // Match expressions like `a * b`, `a / b`, `operator*(a, b)`, and + // `operator/(a, b)` where `a` has type `absl::Duration` and `b` is not of a + // built-in type. + 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")), TemplateInstantiationFilter), + this); + + // Match expressions like `a * b` and `operator*(a, b)` where `a` is not of a + // built-in type and `b` has type `absl::Duration`. + 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"))))), + TemplateInstantiationFilter), + 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 conversion 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. + + // Match calls like: + // `absl::Nanoseconds(x)` + // `absl::Microseconds(x)` + // `absl::Milliseconds(x)` + // `absl::Seconds(x)` + // `absl::Minutes(x)` + // `absl::Hours(x)` + // where `x` is not of a built-in type. + 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")), + TemplateInstantiationFilter))), + this); +} + +void UpgradeDurationConversionsCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *ArgExpr = Result.Nodes.getNodeAs("arg"); + DiagnosticBuilder Diag = diag(ArgExpr->getBeginLoc(), + "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() || + Result.Nodes.getNodeAs("instantiated-expr") != nullptr) { + // An invalid source range likely means we are inside a macro body. + // Otherwise, this means we are inside a template instantiation, and 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. + // + // In either case, we warn but do not suggest a fix since a manual fix is + // likely needed. + return; + } + Diag << FixItHint::CreateInsertion(SourceRange.getBegin(), + "static_cast(") + << FixItHint::CreateInsertion(SourceRange.getEnd(), ")"); +} + +} // 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,42 @@ +.. 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 @@ -12,6 +12,7 @@ abseil-redundant-strcat-calls abseil-string-find-startswith abseil-str-cat-append + abseil-upgrade-duration-conversions android-cloexec-accept android-cloexec-accept4 android-cloexec-creat 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,451 @@ +// 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; +}; + +template +ConvertibleTo operator+(ConvertibleTo, ConvertibleTo); + +template +ConvertibleTo operator*(ConvertibleTo, ConvertibleTo); + +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())); + ConvertibleTo c; + d *= (c + c) * c + c; + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: d *= static_cast((c + c) * c + c) + d /= (c + c) * c + c; + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: d /= static_cast((c + c) * c + c) + d = d * c * c; + // CHECK-MESSAGES: [[@LINE-1]]:11: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-MESSAGES: [[@LINE-2]]:15: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: d = d * static_cast(c) * static_cast(c) + d = c * d * c; + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-MESSAGES: [[@LINE-2]]:15: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: d = static_cast(c) * d * static_cast(c) + d = d / c * c; + // CHECK-MESSAGES: [[@LINE-1]]:11: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-MESSAGES: [[@LINE-2]]:15: warning: perform explicit cast on expression getting implicitly converted to int64_t + // CHECK-FIXES: d = d / static_cast(c) * static_cast(c) +} + +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 arithmeticNonTypeTemplateParamSpecialization() { + absl::Duration d; + d *= N; +} + +template <> void arithmeticNonTypeTemplateParamSpecialization<5>() { + 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()); + arithmeticNonTypeTemplateParamSpecialization<1>(); + arithmeticNonTypeTemplateParamSpecialization<5>(); + 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() + +#define T_OBJECT T() +#define T_CALL_EXPR d *= T() + +template void arithmeticTemplateAndMacro() { + absl::Duration d; + d *= T_OBJECT; + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: perform explicit cast on expression getting implicitly converted to int64_t + 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); + T_CALL_EXPR; + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: perform explicit cast on expression getting implicitly converted to int64_t +} + +#define TEMPLATE_MACRO(type) \ + template void TemplateInMacro(T t) { \ + type d; \ + d *= t; \ + } + +TEMPLATE_MACRO(absl::Duration) +// CHECK-MESSAGES: [[@LINE-1]]:1: warning: perform explicit cast on expression getting implicitly converted to int64_t + +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 + arithmeticTemplateAndMacro>(); + TemplateInMacro(ConvertibleTo()); +} + +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 factoryNonTypeTemplateParamSpecialization() { + (void)absl::Nanoseconds(N); +} + +template <> void factoryNonTypeTemplateParamSpecialization<5>() { + (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 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()); + factoryNonTypeTemplateParamSpecialization<1>(); + factoryNonTypeTemplateParamSpecialization<5>(); + templateFactoryFix(); + templateFactoryWarnOnly(ConvertibleTo()); + TemplateTypeFactoryWarnOnly> t; + t.memberA(ConvertibleTo()); + t.memberB(ConvertibleTo()); +} + +#define ONLY_WARN_INSIDE_MACRO_FACTORY \ + (void)absl::Nanoseconds(ConvertibleTo()) +#define T_CALL_FACTORTY_INSIDE_MACRO (void)absl::Nanoseconds(T()) + +template void factoryTemplateAndMacro() { + (void)absl::Nanoseconds(T_OBJECT); + // CHECK-MESSAGES: [[@LINE-1]]:27: warning: perform explicit cast on expression getting implicitly converted to int64_t + (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)) + T_CALL_FACTORTY_INSIDE_MACRO; + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: perform explicit cast on expression getting implicitly converted to int64_t +} + +#define TEMPLATE_FACTORY_MACRO(factory) \ + template void TemplateFactoryInMacro(T t) { (void)factory(t); } + +TEMPLATE_FACTORY_MACRO(absl::Nanoseconds) +// CHECK-MESSAGES: [[@LINE-1]]:1: warning: perform explicit cast on expression getting implicitly converted to int64_t + +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 + factoryTemplateAndMacro>(); + TemplateFactoryInMacro(ConvertibleTo()); +}