astrelni updated this revision to Diff 172402. astrelni added a comment. Herald added a subscriber: mgrang.
Updated filtering of template instantiations to not use potentially costly hasDescendent matcher. https://reviews.llvm.org/D53830 Files: clang-tidy/abseil/AbseilTidyModule.cpp clang-tidy/abseil/CMakeLists.txt clang-tidy/abseil/UpgradeDurationConversionsCheck.cpp clang-tidy/abseil/UpgradeDurationConversionsCheck.h docs/ReleaseNotes.rst docs/clang-tidy/checks/abseil-upgrade-duration-conversions.rst docs/clang-tidy/checks/list.rst test/clang-tidy/abseil-upgrade-duration-conversions.cpp
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: 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<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. 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 <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; +}; + +void arithmeticOperatorsInTemplates() { + templateForOpsSpecialization(5); + templateForOpsSpecialization(absl::Duration()); + arithmeticNonTypeTemplateParamSpecialization<1>(); + arithmeticNonTypeTemplateParamSpecialization<5>(); + templateOpsFix<int>(); + templateOpsWarnOnly(absl::Duration(), ConvertibleTo<int>()); + TemplateTypeOpsWarnOnly<ConvertibleTo<int>> t; + t.memberA(ConvertibleTo<int>()); + t.memberB(absl::Duration(), ConvertibleTo<int>()); +} + +#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> 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>()); + TemplateTypeFactoryWarnOnly<ConvertibleTo<int>> t; + t.memberA(ConvertibleTo<int>()); + t.memberB(ConvertibleTo<int>()); +} + +#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>()); +} 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 + <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. 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<StringFindStartswithCheck>( "abseil-string-find-startswith"); + CheckFactories.registerCheck<UpgradeDurationConversionsCheck>( + "abseil-upgrade-duration-conversions"); } }; Index: clang-tidy/abseil/UpgradeDurationConversionsCheck.cpp =================================================================== --- clang-tidy/abseil/UpgradeDurationConversionsCheck.cpp +++ clang-tidy/abseil/UpgradeDurationConversionsCheck.cpp @@ -0,0 +1,164 @@ +//===--- 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()))), + anyOf(hasName("operator*="), hasName("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()))), + anyOf(hasName("operator*="), hasName("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()))), + anyOf(hasName("::absl::operator*"), + hasName("::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); +} + +static const llvm::StringRef Message = + "implicit conversion to 'int64_t' is deprecated in this context; use an " + "explicit cast instead"; + +void UpgradeDurationConversionsCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *ArgExpr = Result.Nodes.getNodeAs<Expr>("arg"); + + // We gather source locations from template matches inside and outside of + // template instantiations for later analysis. + if (!match(isInTemplateInstantiation(), *ArgExpr, *Result.Context).empty()) { + MatchedInstantiationLocations.push_back(ArgExpr->getBeginLoc()); + return; + } + internal::Matcher<Stmt> IsInsideTemplate = + hasAncestor(decl(anyOf(classTemplateDecl(), functionTemplateDecl()))); + if (!match(IsInsideTemplate, *ArgExpr, *Result.Context).empty()) + MatchedTemplateLocations.push_back(ArgExpr->getBeginLoc()); + + DiagnosticBuilder Diag = diag(ArgExpr->getBeginLoc(), 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(), ")"); +} + +void UpgradeDurationConversionsCheck::onEndOfTranslationUnit() { + // 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. + std::sort(MatchedTemplateLocations.begin(), MatchedTemplateLocations.end()); + for (SourceLocation Loc : MatchedInstantiationLocations) { + if (!std::binary_search(MatchedTemplateLocations.begin(), + MatchedTemplateLocations.end(), Loc)) + diag(Loc, Message); + } +} + +} // namespace abseil +} // namespace tidy +} // namespace clang Index: clang-tidy/abseil/UpgradeDurationConversionsCheck.h =================================================================== --- clang-tidy/abseil/UpgradeDurationConversionsCheck.h +++ clang-tidy/abseil/UpgradeDurationConversionsCheck.h @@ -0,0 +1,42 @@ +//===--- 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 <vector> + +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; + void onEndOfTranslationUnit() override; + +private: + std::vector<SourceLocation> MatchedTemplateLocations; + std::vector<SourceLocation> MatchedInstantiationLocations; +}; + +} // namespace abseil +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_UPGRADEDURATIONCONVERSIONSCHECK_H 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
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits