Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[clang-tidy] Add the abseil-time-compare check
This is an analog of the abseil-duration-comparison check, but for the absl::Time domain. It has a similar implementation and tests. Differential Revision: https://reviews.llvm.org/D58977 llvm-svn: 355835
- Loading branch information
Hyrum Wright
committed
Mar 11, 2019
1 parent
7fd99fc
commit 1603447
Showing
10 changed files
with
273 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
61 changes: 61 additions & 0 deletions
61
clang-tools-extra/clang-tidy/abseil/TimeComparisonCheck.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
//===--- TimeComparisonCheck.cpp - clang-tidy | ||
//--------------------------------===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "TimeComparisonCheck.h" | ||
#include "DurationRewriter.h" | ||
#include "clang/AST/ASTContext.h" | ||
#include "clang/ASTMatchers/ASTMatchFinder.h" | ||
#include "clang/Tooling/FixIt.h" | ||
|
||
using namespace clang::ast_matchers; | ||
|
||
namespace clang { | ||
namespace tidy { | ||
namespace abseil { | ||
|
||
void TimeComparisonCheck::registerMatchers(MatchFinder *Finder) { | ||
auto Matcher = | ||
expr(comparisonOperatorWithCallee(functionDecl( | ||
functionDecl(TimeConversionFunction()).bind("function_decl")))) | ||
.bind("binop"); | ||
|
||
Finder->addMatcher(Matcher, this); | ||
} | ||
|
||
void TimeComparisonCheck::check(const MatchFinder::MatchResult &Result) { | ||
const auto *Binop = Result.Nodes.getNodeAs<BinaryOperator>("binop"); | ||
|
||
llvm::Optional<DurationScale> Scale = getScaleForTimeInverse( | ||
Result.Nodes.getNodeAs<FunctionDecl>("function_decl")->getName()); | ||
if (!Scale) | ||
return; | ||
|
||
if (isInMacro(Result, Binop->getLHS()) || isInMacro(Result, Binop->getRHS())) | ||
return; | ||
|
||
// In most cases, we'll only need to rewrite one of the sides, but we also | ||
// want to handle the case of rewriting both sides. This is much simpler if | ||
// we unconditionally try and rewrite both, and let the rewriter determine | ||
// if nothing needs to be done. | ||
std::string LhsReplacement = | ||
rewriteExprFromNumberToTime(Result, *Scale, Binop->getLHS()); | ||
std::string RhsReplacement = | ||
rewriteExprFromNumberToTime(Result, *Scale, Binop->getRHS()); | ||
|
||
diag(Binop->getBeginLoc(), "perform comparison in the time domain") | ||
<< FixItHint::CreateReplacement(Binop->getSourceRange(), | ||
(llvm::Twine(LhsReplacement) + " " + | ||
Binop->getOpcodeStr() + " " + | ||
RhsReplacement) | ||
.str()); | ||
} | ||
|
||
} // namespace abseil | ||
} // namespace tidy | ||
} // namespace clang |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
//===--- TimeComparisonCheck.h - clang-tidy ---------------------*- C++ -*-===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_TIMECOMPARECHECK_H | ||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_TIMECOMPARECHECK_H | ||
|
||
#include "../ClangTidy.h" | ||
|
||
namespace clang { | ||
namespace tidy { | ||
namespace abseil { | ||
|
||
/// Prefer comparison in the `absl::Time` domain instead of the numeric | ||
/// domain. | ||
/// | ||
/// For the user-facing documentation see: | ||
/// http://clang.llvm.org/extra/clang-tidy/checks/abseil-time-comparison.html | ||
class TimeComparisonCheck : public ClangTidyCheck { | ||
public: | ||
TimeComparisonCheck(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_TIMECOMPARECHECK_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 23 additions & 0 deletions
23
clang-tools-extra/docs/clang-tidy/checks/abseil-time-comparison.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
.. title:: clang-tidy - abseil-time-comparison | ||
|
||
abseil-time-comparison | ||
====================== | ||
|
||
Prefer comparisons in the ``absl::Time`` domain instead of the integer domain. | ||
|
||
N.B.: In cases where an ``absl::Time`` is being converted to an integer, | ||
alignment may occur. If the comparison depends on this alingment, doing the | ||
comparison in the ``absl::Time`` domain may yield a different result. In | ||
practice this is very rare, and still indicates a bug which should be fixed. | ||
|
||
Examples: | ||
|
||
.. code-block:: c++ | ||
|
||
// Original - Comparison in the integer domain | ||
int x; | ||
absl::Time t; | ||
if (x < absl::ToUnixSeconds(t)) ... | ||
|
||
// Suggested - Compare in the absl::Time domain instead | ||
if (absl::FromUnixSeconds(x) < t) ... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
129 changes: 129 additions & 0 deletions
129
clang-tools-extra/test/clang-tidy/abseil-time-comparison.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
// RUN: %check_clang_tidy %s abseil-time-comparison %t -- -- -I%S/Inputs | ||
|
||
#include "absl/time/time.h" | ||
|
||
void f() { | ||
double x; | ||
absl::Duration d1, d2; | ||
bool b; | ||
absl::Time t1, t2; | ||
|
||
// Check against the RHS | ||
b = x > absl::ToUnixSeconds(t1); | ||
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform comparison in the time domain [abseil-time-comparison] | ||
// CHECK-FIXES: absl::FromUnixSeconds(x) > t1; | ||
b = x >= absl::ToUnixSeconds(t1); | ||
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform comparison in the time domain [abseil-time-comparison] | ||
// CHECK-FIXES: absl::FromUnixSeconds(x) >= t1; | ||
b = x == absl::ToUnixSeconds(t1); | ||
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform comparison in the time domain [abseil-time-comparison] | ||
// CHECK-FIXES: absl::FromUnixSeconds(x) == t1; | ||
b = x <= absl::ToUnixSeconds(t1); | ||
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform comparison in the time domain [abseil-time-comparison] | ||
// CHECK-FIXES: absl::FromUnixSeconds(x) <= t1; | ||
b = x < absl::ToUnixSeconds(t1); | ||
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform comparison in the time domain [abseil-time-comparison] | ||
// CHECK-FIXES: absl::FromUnixSeconds(x) < t1; | ||
b = x == absl::ToUnixSeconds(t1 - d2); | ||
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform comparison in the time domain [abseil-time-comparison] | ||
// CHECK-FIXES: absl::FromUnixSeconds(x) == t1 - d2; | ||
b = absl::ToUnixSeconds(t1) > absl::ToUnixSeconds(t2); | ||
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform comparison in the time domain [abseil-time-comparison] | ||
// CHECK-FIXES: t1 > t2; | ||
|
||
// Check against the LHS | ||
b = absl::ToUnixSeconds(t1) < x; | ||
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform comparison in the time domain [abseil-time-comparison] | ||
// CHECK-FIXES: t1 < absl::FromUnixSeconds(x); | ||
b = absl::ToUnixSeconds(t1) <= x; | ||
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform comparison in the time domain [abseil-time-comparison] | ||
// CHECK-FIXES: t1 <= absl::FromUnixSeconds(x); | ||
b = absl::ToUnixSeconds(t1) == x; | ||
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform comparison in the time domain [abseil-time-comparison] | ||
// CHECK-FIXES: t1 == absl::FromUnixSeconds(x); | ||
b = absl::ToUnixSeconds(t1) >= x; | ||
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform comparison in the time domain [abseil-time-comparison] | ||
// CHECK-FIXES: t1 >= absl::FromUnixSeconds(x); | ||
b = absl::ToUnixSeconds(t1) > x; | ||
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform comparison in the time domain [abseil-time-comparison] | ||
// CHECK-FIXES: t1 > absl::FromUnixSeconds(x); | ||
|
||
// Comparison against zero | ||
b = absl::ToUnixSeconds(t1) < 0.0; | ||
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform comparison in the time domain [abseil-time-comparison] | ||
// CHECK-FIXES: t1 < absl::UnixEpoch(); | ||
b = absl::ToUnixSeconds(t1) < 0; | ||
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform comparison in the time domain [abseil-time-comparison] | ||
// CHECK-FIXES: t1 < absl::UnixEpoch(); | ||
|
||
// Scales other than Seconds | ||
b = x > absl::ToUnixMicros(t1); | ||
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform comparison in the time domain [abseil-time-comparison] | ||
// CHECK-FIXES: absl::FromUnixMicros(x) > t1; | ||
b = x >= absl::ToUnixMillis(t1); | ||
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform comparison in the time domain [abseil-time-comparison] | ||
// CHECK-FIXES: absl::FromUnixMillis(x) >= t1; | ||
b = x == absl::ToUnixNanos(t1); | ||
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform comparison in the time domain [abseil-time-comparison] | ||
// CHECK-FIXES: absl::FromUnixNanos(x) == t1; | ||
b = x <= absl::ToUnixMinutes(t1); | ||
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform comparison in the time domain [abseil-time-comparison] | ||
// CHECK-FIXES: absl::FromUnixMinutes(x) <= t1; | ||
b = x < absl::ToUnixHours(t1); | ||
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform comparison in the time domain [abseil-time-comparison] | ||
// CHECK-FIXES: absl::FromUnixHours(x) < t1; | ||
|
||
// A long expression | ||
bool some_condition; | ||
int very_very_very_very_long_variable_name; | ||
absl::Time SomeTime; | ||
if (some_condition && very_very_very_very_long_variable_name | ||
< absl::ToUnixSeconds(SomeTime)) { | ||
// CHECK-MESSAGES: [[@LINE-2]]:25: warning: perform comparison in the time domain [abseil-time-comparison] | ||
// CHECK-FIXES: if (some_condition && absl::FromUnixSeconds(very_very_very_very_long_variable_name) < SomeTime) { | ||
return; | ||
} | ||
|
||
// A complex expression | ||
int y; | ||
b = (y + 5) * 10 > absl::ToUnixMillis(t1); | ||
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform comparison in the time domain [abseil-time-comparison] | ||
// CHECK-FIXES: absl::FromUnixMillis((y + 5) * 10) > t1; | ||
|
||
// We should still transform the expression inside this macro invocation | ||
#define VALUE_IF(v, e) v ? (e) : 0 | ||
int a = VALUE_IF(1, 5 > absl::ToUnixSeconds(t1)); | ||
// CHECK-MESSAGES: [[@LINE-1]]:23: warning: perform comparison in the time domain [abseil-time-comparison] | ||
// CHECK-FIXES: VALUE_IF(1, absl::FromUnixSeconds(5) > t1); | ||
#undef VALUE_IF | ||
|
||
#define VALUE_IF_2(e) (e) | ||
#define VALUE_IF(v, e) v ? VALUE_IF_2(e) : VALUE_IF_2(0) | ||
int a2 = VALUE_IF(1, 5 > absl::ToUnixSeconds(t1)); | ||
// CHECK-MESSAGES: [[@LINE-1]]:24: warning: perform comparison in the time domain [abseil-time-comparison] | ||
// CHECK-FIXES: VALUE_IF(1, absl::FromUnixSeconds(5) > t1); | ||
#undef VALUE_IF | ||
#undef VALUE_IF_2 | ||
|
||
#define VALUE_IF_2(e) (e) | ||
#define VALUE_IF(v, e, type) (v ? VALUE_IF_2(absl::To##type##Seconds(e)) : 0) | ||
int a3 = VALUE_IF(1, t1, Unix); | ||
#undef VALUE_IF | ||
#undef VALUE_IF_2 | ||
|
||
#define VALUE_IF_2(e) (e) | ||
#define VALUE_IF(v, e, type) (v ? (5 > VALUE_IF_2(absl::To##type##Seconds(e))) : 0) | ||
int a4 = VALUE_IF(1, t1, Unix); | ||
#undef VALUE_IF | ||
#undef VALUE_IF_2 | ||
|
||
// These should not match | ||
b = 6 < 4; | ||
|
||
#define TODOUBLE(x) absl::ToUnixSeconds(x) | ||
b = 5.0 > TODOUBLE(t1); | ||
#undef TODOUBLE | ||
#define THIRTY 30.0 | ||
b = THIRTY > absl::ToUnixSeconds(t1); | ||
#undef THIRTY | ||
} |