diff options
8 files changed, 726 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp b/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp index a559ac5f8fe..23fc2cf2770 100644 --- a/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp @@ -20,6 +20,7 @@  #include "RedundantStrcatCallsCheck.h"  #include "StringFindStartswithCheck.h"  #include "StrCatAppendCheck.h" +#include "UpgradeDurationConversionsCheck.h"  namespace clang {  namespace tidy { @@ -47,6 +48,8 @@ public:          "abseil-str-cat-append");      CheckFactories.registerCheck<StringFindStartswithCheck>(          "abseil-string-find-startswith"); +    CheckFactories.registerCheck<UpgradeDurationConversionsCheck>( +        "abseil-upgrade-duration-conversions");    }  }; diff --git a/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt b/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt index 1139f2e1797..34640318a22 100644 --- a/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt @@ -13,6 +13,7 @@ add_clang_library(clangTidyAbseilModule    RedundantStrcatCallsCheck.cpp    StrCatAppendCheck.cpp    StringFindStartswithCheck.cpp +  UpgradeDurationConversionsCheck.cpp    LINK_LIBS    clangAST diff --git a/clang-tools-extra/clang-tidy/abseil/UpgradeDurationConversionsCheck.cpp b/clang-tools-extra/clang-tidy/abseil/UpgradeDurationConversionsCheck.cpp new file mode 100644 index 00000000000..bdf32bccc37 --- /dev/null +++ b/clang-tools-extra/clang-tidy/abseil/UpgradeDurationConversionsCheck.cpp @@ -0,0 +1,158 @@ +//===--- 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(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. + +  // 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()))), +              hasAnyName("operator*=", "operator/=")))), +      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()))), +              hasAnyName("operator*=", "operator/="))), +          argumentCountIs(1), hasArgument(0, expr().bind("arg"))), +      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()))), +                   hasAnyName("::absl::operator*", "::absl::operator/"))), +               argumentCountIs(2), +               hasArgument(0, expr(hasType( +                                  cxxRecordDecl(hasName("::absl::Duration"))))), +               hasArgument(1, expr().bind("arg"))), +      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")))))), +      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( +                  hasAnyName("::absl::Nanoseconds", "::absl::Microseconds", +                             "::absl::Milliseconds", "::absl::Seconds", +                             "::absl::Minutes", "::absl::Hours"), +                  unless(hasParent(functionTemplateDecl())))), +              hasArgument(0, expr().bind("arg"))))), +      this); +} + +void UpgradeDurationConversionsCheck::check( +    const MatchFinder::MatchResult &Result) { +  const llvm::StringRef Message = +      "implicit conversion to 'int64_t' is deprecated in this context; use an " +      "explicit cast instead"; + +  const auto *ArgExpr = Result.Nodes.getNodeAs<Expr>("arg"); +  SourceLocation Loc = ArgExpr->getBeginLoc(); + +  if (!match(isInTemplateInstantiation(), *ArgExpr, *Result.Context).empty()) { +    if (MatchedTemplateLocations.count(Loc.getRawEncoding()) == 0) { +      // For each location matched in a template instantiation, we check if the +      // location can also be found in `MatchedTemplateLocations`. If it is not +      // found, that means the expression did not create a match without the +      // instantiation and depends on template parameters. A manual fix is +      // probably required so we provide only a warning. +      diag(Loc, Message); +    } +    return; +  } + +  // We gather source locations from template matches not in template +  // instantiations for future matches. +  internal::Matcher<Stmt> IsInsideTemplate = +      hasAncestor(decl(anyOf(classTemplateDecl(), functionTemplateDecl()))); +  if (!match(IsInsideTemplate, *ArgExpr, *Result.Context).empty()) +    MatchedTemplateLocations.insert(Loc.getRawEncoding()); + +  DiagnosticBuilder Diag = diag(Loc, Message); +  CharSourceRange SourceRange = Lexer::makeFileCharRange( +      CharSourceRange::getTokenRange(ArgExpr->getSourceRange()), +      *Result.SourceManager, Result.Context->getLangOpts()); +  if (SourceRange.isInvalid()) +    // An invalid source range likely means we are inside a macro body. A manual +    // fix is likely needed so we do not create a fix-it hint. +    return; + +  Diag << FixItHint::CreateInsertion(SourceRange.getBegin(), +                                     "static_cast<int64_t>(") +       << FixItHint::CreateInsertion(SourceRange.getEnd(), ")"); +} + +} // namespace abseil +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/abseil/UpgradeDurationConversionsCheck.h b/clang-tools-extra/clang-tidy/abseil/UpgradeDurationConversionsCheck.h new file mode 100644 index 00000000000..63712e2caef --- /dev/null +++ b/clang-tools-extra/clang-tidy/abseil/UpgradeDurationConversionsCheck.h @@ -0,0 +1,40 @@ +//===--- 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" + +#include <unordered_set> + +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; + +private: +  std::unordered_set<unsigned> MatchedTemplateLocations; +}; + +} // namespace abseil +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_UPGRADEDURATIONCONVERSIONSCHECK_H diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index cec9645fe65..e1c23d66b55 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -129,6 +129,13 @@ Improvements to clang-tidy    which means this type can't represent all values which are part of the    iteration range. +- New :doc:`abseil-upgrade-duration-conversions +  <clang-tidy/checks/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    <clang-tidy/checks/cppcoreguidelines-macro-usage>` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/abseil-upgrade-duration-conversions.rst b/clang-tools-extra/docs/clang-tidy/checks/abseil-upgrade-duration-conversions.rst new file mode 100644 index 00000000000..24e557d2edc --- /dev/null +++ b/clang-tools-extra/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<float>`` 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. + +These operators and factories will be changed to only accept arithmetic types to +prevent unintended behavior. After these changes are released, passing an +argument of class type will no longer compile, even if the type is implicitly +convertible to an arithmetic type. + +Here are example fixes created by this check: + +.. code-block:: c++ + +  std::atomic<int> a; +  absl::Duration d = absl::Milliseconds(a); +  d *= a; + +becomes + +.. code-block:: c++ + +  std::atomic<int> a; +  absl::Duration d = absl::Milliseconds(static_cast<int64_t>(a)); +  d *= static_cast<int64_t>(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. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index e3528ae90e8..7febb37720a 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -14,6 +14,7 @@ Clang-Tidy Checks     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 diff --git a/clang-tools-extra/test/clang-tidy/abseil-upgrade-duration-conversions.cpp b/clang-tools-extra/test/clang-tidy/abseil-upgrade-duration-conversions.cpp new file mode 100644 index 00000000000..3ac113c7902 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/abseil-upgrade-duration-conversions.cpp @@ -0,0 +1,473 @@ +// 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 <typename T> Duration &operator*=(T r); + +  Duration &operator/=(int64_t r); +  Duration &operator/=(float r); +  Duration &operator/=(double r); +  template <typename T> Duration &operator/=(T r); +}; + +template <typename T> Duration operator*(Duration lhs, T rhs); +template <typename T> Duration operator*(T lhs, Duration rhs); +template <typename T> 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 <typename T> struct EnableIfFloatImpl {}; +template <> struct EnableIfFloatImpl<float> { typedef int Type; }; +template <> struct EnableIfFloatImpl<double> { typedef int Type; }; +template <> struct EnableIfFloatImpl<long double> { typedef int Type; }; +template <typename T> using EnableIfFloat = typename EnableIfFloatImpl<T>::Type; + +template <typename T, EnableIfFloat<T> = 0> Duration Nanoseconds(T n); +template <typename T, EnableIfFloat<T> = 0> Duration Microseconds(T n); +template <typename T, EnableIfFloat<T> = 0> Duration Milliseconds(T n); +template <typename T, EnableIfFloat<T> = 0> Duration Seconds(T n); +template <typename T, EnableIfFloat<T> = 0> Duration Minutes(T n); +template <typename T, EnableIfFloat<T> = 0> Duration Hours(T n); + +} // namespace absl + +template <typename T> struct ConvertibleTo { +  operator T() const; +}; + +template <typename T> +ConvertibleTo<T> operator+(ConvertibleTo<T>, ConvertibleTo<T>); + +template <typename T> +ConvertibleTo<T> operator*(ConvertibleTo<T>, ConvertibleTo<T>); + +void arithmeticOperatorBasicPositive() { +  absl::Duration d; +  d *= ConvertibleTo<int64_t>(); +  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: d *= static_cast<int64_t>(ConvertibleTo<int64_t>()); +  d /= ConvertibleTo<int64_t>(); +  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: d /= static_cast<int64_t>(ConvertibleTo<int64_t>()); +  d = ConvertibleTo<int64_t>() * d; +  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: d = static_cast<int64_t>(ConvertibleTo<int64_t>()) * d; +  d = d * ConvertibleTo<int64_t>(); +  // CHECK-MESSAGES: [[@LINE-1]]:11: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: d = d * static_cast<int64_t>(ConvertibleTo<int64_t>()); +  d = d / ConvertibleTo<int64_t>(); +  // CHECK-MESSAGES: [[@LINE-1]]:11: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: d = d / static_cast<int64_t>(ConvertibleTo<int64_t>()); +  d.operator*=(ConvertibleTo<int64_t>()); +  // CHECK-MESSAGES: [[@LINE-1]]:16: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: d.operator*=(static_cast<int64_t>(ConvertibleTo<int64_t>())); +  d.operator/=(ConvertibleTo<int64_t>()); +  // CHECK-MESSAGES: [[@LINE-1]]:16: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: d.operator/=(static_cast<int64_t>(ConvertibleTo<int64_t>())); +  d = operator*(ConvertibleTo<int64_t>(), d); +  // CHECK-MESSAGES: [[@LINE-1]]:17: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: d = operator*(static_cast<int64_t>(ConvertibleTo<int64_t>()), d); +  d = operator*(d, ConvertibleTo<int64_t>()); +  // CHECK-MESSAGES: [[@LINE-1]]:20: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: d = operator*(d, static_cast<int64_t>(ConvertibleTo<int64_t>())); +  d = operator/(d, ConvertibleTo<int64_t>()); +  // CHECK-MESSAGES: [[@LINE-1]]:20: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: d = operator/(d, static_cast<int64_t>(ConvertibleTo<int64_t>())); +  ConvertibleTo<int64_t> c; +  d *= (c + c) * c + c; +  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: d *= static_cast<int64_t>((c + c) * c + c) +  d /= (c + c) * c + c; +  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: d /= static_cast<int64_t>((c + c) * c + c) +  d = d * c * c; +  // CHECK-MESSAGES: [[@LINE-1]]:11: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-MESSAGES: [[@LINE-2]]:15: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: d = d * static_cast<int64_t>(c) * static_cast<int64_t>(c) +  d = c * d * c; +  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-MESSAGES: [[@LINE-2]]:15: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: d = static_cast<int64_t>(c) * d * static_cast<int64_t>(c) +  d = d / c * c; +  // CHECK-MESSAGES: [[@LINE-1]]:11: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-MESSAGES: [[@LINE-2]]:15: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: d = d / static_cast<int64_t>(c) * static_cast<int64_t>(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<int>(ConvertibleTo<int>()); +  d *= (int)ConvertibleTo<int>(); +  d *= int(ConvertibleTo<int>()); +  d /= static_cast<int>(ConvertibleTo<int>()); +  d /= (int)ConvertibleTo<int>(); +  d /= int(ConvertibleTo<int>()); +  d = static_cast<int>(ConvertibleTo<int>()) * d; +  d = (int)ConvertibleTo<int>() * d; +  d = int(ConvertibleTo<int>()) * d; +  d = d * static_cast<int>(ConvertibleTo<int>()); +  d = d * (int)ConvertibleTo<int>(); +  d = d * int(ConvertibleTo<int>()); +  d = d / static_cast<int>(ConvertibleTo<int>()); +  d = d / (int)ConvertibleTo<int>(); +  d = d / int(ConvertibleTo<int>()); + +  d *= 1 + ConvertibleTo<int>(); +  d /= 1 + ConvertibleTo<int>(); +  d = (1 + ConvertibleTo<int>()) * d; +  d = d * (1 + ConvertibleTo<int>()); +  d = d / (1 + ConvertibleTo<int>()); +} + +template <typename T> void templateForOpsSpecialization(T) {} +template <> +void templateForOpsSpecialization<absl::Duration>(absl::Duration d) { +  d *= ConvertibleTo<int64_t>(); +  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: d *= static_cast<int64_t>(ConvertibleTo<int64_t>()); +} + +template <int N> void arithmeticNonTypeTemplateParamSpecialization() { +  absl::Duration d; +  d *= N; +} + +template <> void arithmeticNonTypeTemplateParamSpecialization<5>() { +  absl::Duration d; +  d *= ConvertibleTo<int>(); +  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: d *= static_cast<int64_t>(ConvertibleTo<int>()); +} + +template <typename T> void templateOpsFix() { +  absl::Duration d; +  d *= ConvertibleTo<int64_t>(); +  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: d *= static_cast<int64_t>(ConvertibleTo<int64_t>()); +} + +template <typename T, typename U> void templateOpsWarnOnly(T t, U u) { +  t *= u; +  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  absl::Duration d; +  d *= u; +  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +} + +template <typename T> struct TemplateTypeOpsWarnOnly { +  void memberA(T t) { +    d *= t; +    // CHECK-MESSAGES: [[@LINE-1]]:10: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  } +  template <typename U, typename V> void memberB(U u, V v) { +    u *= v; +    // CHECK-MESSAGES: [[@LINE-1]]:10: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +    d *= v; +    // CHECK-MESSAGES: [[@LINE-1]]:10: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  } + +  absl::Duration d; +}; + +template <typename T, typename U> +void templateOpsInstantiationBeforeDefinition(T t, U u); + +void arithmeticOperatorsInTemplates() { +  templateForOpsSpecialization(5); +  templateForOpsSpecialization(absl::Duration()); +  arithmeticNonTypeTemplateParamSpecialization<1>(); +  arithmeticNonTypeTemplateParamSpecialization<5>(); +  templateOpsFix<int>(); +  templateOpsWarnOnly(absl::Duration(), ConvertibleTo<int>()); +  templateOpsInstantiationBeforeDefinition(absl::Duration(), +                                           ConvertibleTo<int>()); +  TemplateTypeOpsWarnOnly<ConvertibleTo<int>> t; +  t.memberA(ConvertibleTo<int>()); +  t.memberB(absl::Duration(), ConvertibleTo<int>()); +} + +template <typename T, typename U> +void templateOpsInstantiationBeforeDefinition(T t, U u) { +  t *= u; +  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  absl::Duration d; +  d *= u; +  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +} + +#define FUNCTION_MACRO(x) x +#define CONVERTIBLE_TMP ConvertibleTo<int>() +#define ONLY_WARN_INSIDE_MACRO_ARITHMETIC_OP d *= ConvertibleTo<int>() + +#define T_OBJECT T() +#define T_CALL_EXPR d *= T() + +template <typename T> void arithmeticTemplateAndMacro() { +  absl::Duration d; +  d *= T_OBJECT; +  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  d *= CONVERTIBLE_TMP; +  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: d *= static_cast<int64_t>(CONVERTIBLE_TMP); +  T_CALL_EXPR; +  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +} + +#define TEMPLATE_MACRO(type)                                                   \ +  template <typename T> void TemplateInMacro(T t) {                            \ +    type d;                                                                    \ +    d *= t;                                                                    \ +  } + +TEMPLATE_MACRO(absl::Duration) +// CHECK-MESSAGES: [[@LINE-1]]:1: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead + +void arithmeticOperatorsInMacros() { +  absl::Duration d; +  d = FUNCTION_MACRO(d * ConvertibleTo<int>()); +  // CHECK-MESSAGES: [[@LINE-1]]:26: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: d = FUNCTION_MACRO(d * static_cast<int64_t>(ConvertibleTo<int>())); +  d *= FUNCTION_MACRO(ConvertibleTo<int>()); +  // CHECK-MESSAGES: [[@LINE-1]]:23: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: d *= static_cast<int64_t>(FUNCTION_MACRO(ConvertibleTo<int>())); +  d *= CONVERTIBLE_TMP; +  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: d *= static_cast<int64_t>(CONVERTIBLE_TMP); +  ONLY_WARN_INSIDE_MACRO_ARITHMETIC_OP; +  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  arithmeticTemplateAndMacro<ConvertibleTo<int>>(); +  TemplateInMacro(ConvertibleTo<int>()); +} + +void factoryFunctionPositive() { +  // User defined conversion: +  (void)absl::Nanoseconds(ConvertibleTo<int64_t>()); +  // CHECK-MESSAGES: [[@LINE-1]]:27: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: (void)absl::Nanoseconds(static_cast<int64_t>(ConvertibleTo<int64_t>())); +  (void)absl::Microseconds(ConvertibleTo<int64_t>()); +  // CHECK-MESSAGES: [[@LINE-1]]:28: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: (void)absl::Microseconds(static_cast<int64_t>(ConvertibleTo<int64_t>())); +  (void)absl::Milliseconds(ConvertibleTo<int64_t>()); +  // CHECK-MESSAGES: [[@LINE-1]]:28: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: (void)absl::Milliseconds(static_cast<int64_t>(ConvertibleTo<int64_t>())); +  (void)absl::Seconds(ConvertibleTo<int64_t>()); +  // CHECK-MESSAGES: [[@LINE-1]]:23: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: (void)absl::Seconds(static_cast<int64_t>(ConvertibleTo<int64_t>())); +  (void)absl::Minutes(ConvertibleTo<int64_t>()); +  // CHECK-MESSAGES: [[@LINE-1]]:23: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: (void)absl::Minutes(static_cast<int64_t>(ConvertibleTo<int64_t>())); +  (void)absl::Hours(ConvertibleTo<int64_t>()); +  // CHECK-MESSAGES: [[@LINE-1]]:21: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: (void)absl::Hours(static_cast<int64_t>(ConvertibleTo<int64_t>())); + +  // User defined conversion to integral type, followed by built-in conversion: +  (void)absl::Nanoseconds(ConvertibleTo<char>()); +  // CHECK-MESSAGES: [[@LINE-1]]:27: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: (void)absl::Nanoseconds(static_cast<int64_t>(ConvertibleTo<char>())); +  (void)absl::Microseconds(ConvertibleTo<char>()); +  // CHECK-MESSAGES: [[@LINE-1]]:28: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: (void)absl::Microseconds(static_cast<int64_t>(ConvertibleTo<char>())); +  (void)absl::Milliseconds(ConvertibleTo<char>()); +  // CHECK-MESSAGES: [[@LINE-1]]:28: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: (void)absl::Milliseconds(static_cast<int64_t>(ConvertibleTo<char>())); +  (void)absl::Seconds(ConvertibleTo<char>()); +  // CHECK-MESSAGES: [[@LINE-1]]:23: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: (void)absl::Seconds(static_cast<int64_t>(ConvertibleTo<char>())); +  (void)absl::Minutes(ConvertibleTo<char>()); +  // CHECK-MESSAGES: [[@LINE-1]]:23: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: (void)absl::Minutes(static_cast<int64_t>(ConvertibleTo<char>())); +  (void)absl::Hours(ConvertibleTo<char>()); +  // CHECK-MESSAGES: [[@LINE-1]]:21: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: (void)absl::Hours(static_cast<int64_t>(ConvertibleTo<char>())); + +  // User defined conversion to floating point type, followed by built-in conversion: +  (void)absl::Nanoseconds(ConvertibleTo<float>()); +  // CHECK-MESSAGES: [[@LINE-1]]:27: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: (void)absl::Nanoseconds(static_cast<int64_t>(ConvertibleTo<float>())); +  (void)absl::Microseconds(ConvertibleTo<float>()); +  // CHECK-MESSAGES: [[@LINE-1]]:28: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: (void)absl::Microseconds(static_cast<int64_t>(ConvertibleTo<float>())); +  (void)absl::Milliseconds(ConvertibleTo<float>()); +  // CHECK-MESSAGES: [[@LINE-1]]:28: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: (void)absl::Milliseconds(static_cast<int64_t>(ConvertibleTo<float>())); +  (void)absl::Seconds(ConvertibleTo<float>()); +  // CHECK-MESSAGES: [[@LINE-1]]:23: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: (void)absl::Seconds(static_cast<int64_t>(ConvertibleTo<float>())); +  (void)absl::Minutes(ConvertibleTo<float>()); +  // CHECK-MESSAGES: [[@LINE-1]]:23: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: (void)absl::Minutes(static_cast<int64_t>(ConvertibleTo<float>())); +  (void)absl::Hours(ConvertibleTo<float>()); +  // CHECK-MESSAGES: [[@LINE-1]]:21: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: (void)absl::Hours(static_cast<int64_t>(ConvertibleTo<float>())); +} + +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<int>(ConvertibleTo<int>())); +  (void)absl::Microseconds(static_cast<int>(ConvertibleTo<int>())); +  (void)absl::Milliseconds(static_cast<int>(ConvertibleTo<int>())); +  (void)absl::Seconds(static_cast<int>(ConvertibleTo<int>())); +  (void)absl::Minutes(static_cast<int>(ConvertibleTo<int>())); +  (void)absl::Hours(static_cast<int>(ConvertibleTo<int>())); +} + +template <typename T> void templateForFactorySpecialization(T) {} +template <> void templateForFactorySpecialization<ConvertibleTo<int>>(ConvertibleTo<int> c) { +  (void)absl::Nanoseconds(c); +  // CHECK-MESSAGES: [[@LINE-1]]:27: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: (void)absl::Nanoseconds(static_cast<int64_t>(c)); +} + +template <int N> void factoryNonTypeTemplateParamSpecialization() { +  (void)absl::Nanoseconds(N); +} + +template <> void factoryNonTypeTemplateParamSpecialization<5>() { +  (void)absl::Nanoseconds(ConvertibleTo<int>()); +  // CHECK-MESSAGES: [[@LINE-1]]:27: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: (void)absl::Nanoseconds(static_cast<int64_t>(ConvertibleTo<int>())); +} + +template <typename T> void templateFactoryFix() { +  (void)absl::Nanoseconds(ConvertibleTo<int>()); +  // CHECK-MESSAGES: [[@LINE-1]]:27: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: (void)absl::Nanoseconds(static_cast<int64_t>(ConvertibleTo<int>())); +} + +template <typename T> void templateFactoryWarnOnly(T t) { +  (void)absl::Nanoseconds(t); +  // CHECK-MESSAGES: [[@LINE-1]]:27: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +} + +template <typename T> void templateFactoryInstantiationBeforeDefinition(T t); + +template <typename T> struct TemplateTypeFactoryWarnOnly { +  void memberA(T t) { +    (void)absl::Nanoseconds(t); +    // CHECK-MESSAGES: [[@LINE-1]]:29: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  } +  template <typename U> void memberB(U u) { +    (void)absl::Nanoseconds(u); +    // CHECK-MESSAGES: [[@LINE-1]]:29: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  } +}; + +void factoryInTemplates() { +  templateForFactorySpecialization(5); +  templateForFactorySpecialization(ConvertibleTo<int>()); +  factoryNonTypeTemplateParamSpecialization<1>(); +  factoryNonTypeTemplateParamSpecialization<5>(); +  templateFactoryFix<int>(); +  templateFactoryWarnOnly(ConvertibleTo<int>()); +  templateFactoryInstantiationBeforeDefinition(ConvertibleTo<int>()); +  TemplateTypeFactoryWarnOnly<ConvertibleTo<int>> t; +  t.memberA(ConvertibleTo<int>()); +  t.memberB(ConvertibleTo<int>()); +} + +template <typename T> void templateFactoryInstantiationBeforeDefinition(T t) { +  (void)absl::Nanoseconds(t); +  // CHECK-MESSAGES: [[@LINE-1]]:27: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +} + +#define ONLY_WARN_INSIDE_MACRO_FACTORY                                         \ +  (void)absl::Nanoseconds(ConvertibleTo<int>()) +#define T_CALL_FACTORTY_INSIDE_MACRO (void)absl::Nanoseconds(T()) + +template <typename T> void factoryTemplateAndMacro() { +  (void)absl::Nanoseconds(T_OBJECT); +  // CHECK-MESSAGES: [[@LINE-1]]:27: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  (void)absl::Nanoseconds(CONVERTIBLE_TMP); +  // CHECK-MESSAGES: [[@LINE-1]]:27: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: (void)absl::Nanoseconds(static_cast<int64_t>(CONVERTIBLE_TMP)) +  T_CALL_FACTORTY_INSIDE_MACRO; +  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +} + +#define TEMPLATE_FACTORY_MACRO(factory)                                        \ +  template <typename T> void TemplateFactoryInMacro(T t) { (void)factory(t); } + +TEMPLATE_FACTORY_MACRO(absl::Nanoseconds) +// CHECK-MESSAGES: [[@LINE-1]]:1: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead + +void factoryInMacros() { +  (void)absl::Nanoseconds(FUNCTION_MACRO(ConvertibleTo<int>())); +  // CHECK-MESSAGES: [[@LINE-1]]:42: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: (void)absl::Nanoseconds(static_cast<int64_t>(FUNCTION_MACRO(ConvertibleTo<int>()))); +  (void)absl::Nanoseconds(CONVERTIBLE_TMP); +  // CHECK-MESSAGES: [[@LINE-1]]:27: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  // CHECK-FIXES: (void)absl::Nanoseconds(static_cast<int64_t>(CONVERTIBLE_TMP)) +  ONLY_WARN_INSIDE_MACRO_FACTORY; +  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: implicit conversion to 'int64_t' is deprecated in this context; use an explicit cast instead +  factoryTemplateAndMacro<ConvertibleTo<int>>(); +  TemplateFactoryInMacro(ConvertibleTo<int>()); +}  | 

