Skip to content

Commit b60de17

Browse files
author
Hyrum Wright
committed
[clang-tidy] Add abseil-duration-conversion-cast check
Differential Revision: https://reviews.llvm.org/D56532 llvm-svn: 351473
1 parent 4d14502 commit b60de17

File tree

8 files changed

+257
-1
lines changed

8 files changed

+257
-1
lines changed

clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "../ClangTidyModule.h"
1212
#include "../ClangTidyModuleRegistry.h"
1313
#include "DurationComparisonCheck.h"
14+
#include "DurationConversionCastCheck.h"
1415
#include "DurationDivisionCheck.h"
1516
#include "DurationFactoryFloatCheck.h"
1617
#include "DurationFactoryScaleCheck.h"
@@ -32,6 +33,8 @@ class AbseilModule : public ClangTidyModule {
3233
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
3334
CheckFactories.registerCheck<DurationComparisonCheck>(
3435
"abseil-duration-comparison");
36+
CheckFactories.registerCheck<DurationConversionCastCheck>(
37+
"abseil-duration-conversion-cast");
3538
CheckFactories.registerCheck<DurationDivisionCheck>(
3639
"abseil-duration-division");
3740
CheckFactories.registerCheck<DurationFactoryFloatCheck>(

clang-tools-extra/clang-tidy/abseil/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS support)
33
add_clang_library(clangTidyAbseilModule
44
AbseilTidyModule.cpp
55
DurationComparisonCheck.cpp
6+
DurationConversionCastCheck.cpp
67
DurationDivisionCheck.cpp
78
DurationFactoryFloatCheck.cpp
89
DurationFactoryScaleCheck.cpp
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//===--- DurationConversionCastCheck.cpp - clang-tidy ---------------------===//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#include "DurationConversionCastCheck.h"
11+
#include "DurationRewriter.h"
12+
#include "clang/AST/ASTContext.h"
13+
#include "clang/ASTMatchers/ASTMatchFinder.h"
14+
#include "clang/Tooling/FixIt.h"
15+
16+
using namespace clang::ast_matchers;
17+
18+
namespace clang {
19+
namespace tidy {
20+
namespace abseil {
21+
22+
void DurationConversionCastCheck::registerMatchers(MatchFinder *Finder) {
23+
auto CallMatcher = ignoringImpCasts(callExpr(
24+
callee(functionDecl(DurationConversionFunction()).bind("func_decl")),
25+
hasArgument(0, expr().bind("arg"))));
26+
27+
Finder->addMatcher(
28+
expr(anyOf(
29+
cxxStaticCastExpr(hasSourceExpression(CallMatcher)).bind("cast_expr"),
30+
cStyleCastExpr(hasSourceExpression(CallMatcher)).bind("cast_expr"),
31+
cxxFunctionalCastExpr(hasSourceExpression(CallMatcher))
32+
.bind("cast_expr"))),
33+
this);
34+
}
35+
36+
void DurationConversionCastCheck::check(
37+
const MatchFinder::MatchResult &Result) {
38+
const auto *MatchedCast =
39+
Result.Nodes.getNodeAs<ExplicitCastExpr>("cast_expr");
40+
41+
if (!isNotInMacro(Result, MatchedCast))
42+
return;
43+
44+
const auto *FuncDecl = Result.Nodes.getNodeAs<FunctionDecl>("func_decl");
45+
const auto *Arg = Result.Nodes.getNodeAs<Expr>("arg");
46+
StringRef ConversionFuncName = FuncDecl->getName();
47+
48+
llvm::Optional<DurationScale> Scale = getScaleForInverse(ConversionFuncName);
49+
if (!Scale)
50+
return;
51+
52+
// Casting a double to an integer.
53+
if (MatchedCast->getTypeAsWritten()->isIntegerType() &&
54+
ConversionFuncName.contains("Double")) {
55+
llvm::StringRef NewFuncName = getInverseForScale(*Scale).second;
56+
57+
diag(MatchedCast->getBeginLoc(),
58+
"duration should be converted directly to an integer rather than "
59+
"through a type cast")
60+
<< FixItHint::CreateReplacement(
61+
MatchedCast->getSourceRange(),
62+
(llvm::Twine(NewFuncName.substr(2)) + "(" +
63+
tooling::fixit::getText(*Arg, *Result.Context) + ")")
64+
.str());
65+
}
66+
67+
// Casting an integer to a double.
68+
if (MatchedCast->getTypeAsWritten()->isRealFloatingType() &&
69+
ConversionFuncName.contains("Int64")) {
70+
llvm::StringRef NewFuncName = getInverseForScale(*Scale).first;
71+
72+
diag(MatchedCast->getBeginLoc(), "duration should be converted directly to "
73+
"a floating-piont number rather than "
74+
"through a type cast")
75+
<< FixItHint::CreateReplacement(
76+
MatchedCast->getSourceRange(),
77+
(llvm::Twine(NewFuncName.substr(2)) + "(" +
78+
tooling::fixit::getText(*Arg, *Result.Context) + ")")
79+
.str());
80+
}
81+
}
82+
83+
} // namespace abseil
84+
} // namespace tidy
85+
} // namespace clang
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//===--- DurationConversionCastCheck.h - clang-tidy -------------*- C++ -*-===//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_DURATIONCONVERSIONCASTCHECK_H
11+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_DURATIONCONVERSIONCASTCHECK_H
12+
13+
#include "../ClangTidy.h"
14+
15+
namespace clang {
16+
namespace tidy {
17+
namespace abseil {
18+
19+
/// Checks for casts of ``absl::Duration`` conversion functions, and recommends
20+
/// the right conversion function instead.
21+
///
22+
/// For the user-facing documentation see:
23+
/// http://clang.llvm.org/extra/clang-tidy/checks/abseil-duration-conversion-cast.html
24+
class DurationConversionCastCheck : public ClangTidyCheck {
25+
public:
26+
DurationConversionCastCheck(StringRef Name, ClangTidyContext *Context)
27+
: ClangTidyCheck(Name, Context) {}
28+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
29+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
30+
};
31+
32+
} // namespace abseil
33+
} // namespace tidy
34+
} // namespace clang
35+
36+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_DURATIONCONVERSIONCASTCHECK_H

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,11 @@ The improvements are...
6767
Improvements to clang-tidy
6868
--------------------------
6969

70-
- ...
70+
- New :doc:`abseil-duration-conversion-cast
71+
<clang-tidy/checks/abseil-duration-conversion-cast>` check.
72+
73+
Checks for casts of ``absl::Duration`` conversion functions, and recommends
74+
the right conversion function instead.
7175

7276
Improvements to include-fixer
7377
-----------------------------
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
.. title:: clang-tidy - abseil-duration-conversion-cast
2+
3+
abseil-duration-conversion-cast
4+
===============================
5+
6+
Checks for casts of ``absl::Duration`` conversion functions, and recommends
7+
the right conversion function instead.
8+
9+
Examples:
10+
11+
.. code-block:: c++
12+
13+
// Original - Cast from a double to an integer
14+
absl::Duration d;
15+
int i = static_cast<int>(absl::ToDoubleSeconds(d));
16+
17+
// Suggested - Use the integer conversion function directly.
18+
int i = absl::ToInt64Seconds(d);
19+
20+
21+
// Original - Cast from a double to an integer
22+
absl::Duration d;
23+
double x = static_cast<double>(absl::ToInt64Seconds(d));
24+
25+
// Suggested - Use the integer conversion function directly.
26+
double x = absl::ToDoubleSeconds(d);
27+
28+
29+
Note: In the second example, the suggested fix could yield a different result,
30+
as the conversion to integer could truncate. In practice, this is very rare,
31+
and you should use ``absl::Trunc`` to perform this operation explicitly instead.

clang-tools-extra/docs/clang-tidy/checks/list.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Clang-Tidy Checks
55

66
.. toctree::
77
abseil-duration-comparison
8+
abseil-duration-conversion-cast
89
abseil-duration-division
910
abseil-duration-factory-float
1011
abseil-duration-factory-scale
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// RUN: %check_clang_tidy %s abseil-duration-conversion-cast %t -- -- -I%S/Inputs
2+
3+
#include "absl/time/time.h"
4+
5+
void f() {
6+
absl::Duration d1;
7+
double x;
8+
int i;
9+
10+
i = static_cast<int>(absl::ToDoubleHours(d1));
11+
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: duration should be converted directly to an integer rather than through a type cast [abseil-duration-conversion-cast]
12+
// CHECK-FIXES: absl::ToInt64Hours(d1);
13+
x = static_cast<float>(absl::ToInt64Hours(d1));
14+
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: duration should be converted directly to a floating-piont number rather than through a type cast [abseil-duration-conversion-cast]
15+
// CHECK-FIXES: absl::ToDoubleHours(d1);
16+
i = static_cast<int>(absl::ToDoubleMinutes(d1));
17+
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: duration should be converted directly to an integer rather than through a type cast [abseil-duration-conversion-cast]
18+
// CHECK-FIXES: absl::ToInt64Minutes(d1);
19+
x = static_cast<float>(absl::ToInt64Minutes(d1));
20+
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: duration should be converted directly to a floating-piont number rather than through a type cast [abseil-duration-conversion-cast]
21+
// CHECK-FIXES: absl::ToDoubleMinutes(d1);
22+
i = static_cast<int>(absl::ToDoubleSeconds(d1));
23+
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: duration should be converted directly to an integer rather than through a type cast [abseil-duration-conversion-cast]
24+
// CHECK-FIXES: absl::ToInt64Seconds(d1);
25+
x = static_cast<float>(absl::ToInt64Seconds(d1));
26+
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: duration should be converted directly to a floating-piont number rather than through a type cast [abseil-duration-conversion-cast]
27+
// CHECK-FIXES: absl::ToDoubleSeconds(d1);
28+
i = static_cast<int>(absl::ToDoubleMilliseconds(d1));
29+
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: duration should be converted directly to an integer rather than through a type cast [abseil-duration-conversion-cast]
30+
// CHECK-FIXES: absl::ToInt64Milliseconds(d1);
31+
x = static_cast<float>(absl::ToInt64Milliseconds(d1));
32+
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: duration should be converted directly to a floating-piont number rather than through a type cast [abseil-duration-conversion-cast]
33+
// CHECK-FIXES: absl::ToDoubleMilliseconds(d1);
34+
i = static_cast<int>(absl::ToDoubleMicroseconds(d1));
35+
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: duration should be converted directly to an integer rather than through a type cast [abseil-duration-conversion-cast]
36+
// CHECK-FIXES: absl::ToInt64Microseconds(d1);
37+
x = static_cast<float>(absl::ToInt64Microseconds(d1));
38+
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: duration should be converted directly to a floating-piont number rather than through a type cast [abseil-duration-conversion-cast]
39+
// CHECK-FIXES: absl::ToDoubleMicroseconds(d1);
40+
i = static_cast<int>(absl::ToDoubleNanoseconds(d1));
41+
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: duration should be converted directly to an integer rather than through a type cast [abseil-duration-conversion-cast]
42+
// CHECK-FIXES: absl::ToInt64Nanoseconds(d1);
43+
x = static_cast<float>(absl::ToInt64Nanoseconds(d1));
44+
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: duration should be converted directly to a floating-piont number rather than through a type cast [abseil-duration-conversion-cast]
45+
// CHECK-FIXES: absl::ToDoubleNanoseconds(d1);
46+
47+
// Functional-style casts
48+
i = int(absl::ToDoubleHours(d1));
49+
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: duration should be converted directly to an integer rather than through a type cast [abseil-duration-conversion-cast]
50+
// CHECK-FIXES: absl::ToInt64Hours(d1);
51+
x = float(absl::ToInt64Microseconds(d1));
52+
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: duration should be converted directly to a floating-piont number rather than through a type cast [abseil-duration-conversion-cast]
53+
// CHECK-FIXES: absl::ToDoubleMicroseconds(d1);
54+
55+
// C-style casts
56+
i = (int) absl::ToDoubleHours(d1);
57+
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: duration should be converted directly to an integer rather than through a type cast [abseil-duration-conversion-cast]
58+
// CHECK-FIXES: absl::ToInt64Hours(d1);
59+
x = (float) absl::ToInt64Microseconds(d1);
60+
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: duration should be converted directly to a floating-piont number rather than through a type cast [abseil-duration-conversion-cast]
61+
// CHECK-FIXES: absl::ToDoubleMicroseconds(d1);
62+
63+
// Type aliasing
64+
typedef int FancyInt;
65+
typedef float FancyFloat;
66+
67+
FancyInt j = static_cast<FancyInt>(absl::ToDoubleHours(d1));
68+
// CHECK-MESSAGES: [[@LINE-1]]:16: warning: duration should be converted directly to an integer rather than through a type cast [abseil-duration-conversion-cast]
69+
// CHECK-FIXES: absl::ToInt64Hours(d1);
70+
FancyFloat k = static_cast<FancyFloat>(absl::ToInt64Microseconds(d1));
71+
// CHECK-MESSAGES: [[@LINE-1]]:18: warning: duration should be converted directly to a floating-piont number rather than through a type cast [abseil-duration-conversion-cast]
72+
// CHECK-FIXES: absl::ToDoubleMicroseconds(d1);
73+
74+
// Macro handling
75+
// We want to transform things in macro arguments
76+
#define EXTERNAL(x) (x) + 5
77+
i = EXTERNAL(static_cast<int>(absl::ToDoubleSeconds(d1)));
78+
// CHECK-MESSAGES: [[@LINE-1]]:16: warning: duration should be converted directly to an integer rather than through a type cast [abseil-duration-conversion-cast]
79+
// CHECK-FIXES: EXTERNAL(absl::ToInt64Seconds(d1));
80+
#undef EXTERNAL
81+
82+
// We don't want to transform this which get split across macro boundaries
83+
#define SPLIT(x) static_cast<int>((x)) + 5
84+
i = SPLIT(absl::ToDoubleSeconds(d1));
85+
#undef SPLIT
86+
87+
// We also don't want to transform things inside of a macro definition
88+
#define INTERNAL(x) static_cast<int>(absl::ToDoubleSeconds((x))) + 5
89+
i = INTERNAL(d1);
90+
#undef INTERNAL
91+
92+
// These shouldn't be converted
93+
i = static_cast<int>(absl::ToInt64Seconds(d1));
94+
i = static_cast<float>(absl::ToDoubleSeconds(d1));
95+
}

0 commit comments

Comments
 (0)