diff options
Diffstat (limited to 'clang-tools-extra/clang-tidy')
8 files changed, 421 insertions, 93 deletions
diff --git a/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp b/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp index 0dd24af5164..a559ac5f8fe 100644 --- a/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp @@ -10,6 +10,7 @@ #include "../ClangTidy.h" #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" +#include "DurationComparisonCheck.h" #include "DurationDivisionCheck.h" #include "DurationFactoryFloatCheck.h" #include "DurationFactoryScaleCheck.h" @@ -27,6 +28,8 @@ namespace abseil { class AbseilModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + CheckFactories.registerCheck<DurationComparisonCheck>( + "abseil-duration-comparison"); CheckFactories.registerCheck<DurationDivisionCheck>( "abseil-duration-division"); CheckFactories.registerCheck<DurationFactoryFloatCheck>( diff --git a/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt b/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt index 512438c38b0..1139f2e1797 100644 --- a/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt @@ -2,9 +2,11 @@ set(LLVM_LINK_COMPONENTS support) add_clang_library(clangTidyAbseilModule AbseilTidyModule.cpp + DurationComparisonCheck.cpp DurationDivisionCheck.cpp DurationFactoryFloatCheck.cpp DurationFactoryScaleCheck.cpp + DurationRewriter.cpp FasterStrsplitDelimiterCheck.cpp NoInternalDependenciesCheck.cpp NoNamespaceCheck.cpp diff --git a/clang-tools-extra/clang-tidy/abseil/DurationComparisonCheck.cpp b/clang-tools-extra/clang-tidy/abseil/DurationComparisonCheck.cpp new file mode 100644 index 00000000000..d5eb81b0f49 --- /dev/null +++ b/clang-tools-extra/clang-tidy/abseil/DurationComparisonCheck.cpp @@ -0,0 +1,164 @@ +//===--- DurationComparisonCheck.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 "DurationComparisonCheck.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 { + +/// Given the name of an inverse Duration function (e.g., `ToDoubleSeconds`), +/// return its `DurationScale`, or `None` if a match is not found. +static llvm::Optional<DurationScale> getScaleForInverse(llvm::StringRef Name) { + static const llvm::DenseMap<llvm::StringRef, DurationScale> ScaleMap( + {{"ToDoubleHours", DurationScale::Hours}, + {"ToInt64Hours", DurationScale::Hours}, + {"ToDoubleMinutes", DurationScale::Minutes}, + {"ToInt64Minutes", DurationScale::Minutes}, + {"ToDoubleSeconds", DurationScale::Seconds}, + {"ToInt64Seconds", DurationScale::Seconds}, + {"ToDoubleMilliseconds", DurationScale::Milliseconds}, + {"ToInt64Milliseconds", DurationScale::Milliseconds}, + {"ToDoubleMicroseconds", DurationScale::Microseconds}, + {"ToInt64Microseconds", DurationScale::Microseconds}, + {"ToDoubleNanoseconds", DurationScale::Nanoseconds}, + {"ToInt64Nanoseconds", DurationScale::Nanoseconds}}); + + auto ScaleIter = ScaleMap.find(std::string(Name)); + if (ScaleIter == ScaleMap.end()) + return llvm::None; + + return ScaleIter->second; +} + +/// Given a `Scale` return the inverse functions for it. +static const std::pair<llvm::StringRef, llvm::StringRef> & +getInverseForScale(DurationScale Scale) { + static const std::unordered_map<DurationScale, + std::pair<llvm::StringRef, llvm::StringRef>> + InverseMap( + {{DurationScale::Hours, + std::make_pair("::absl::ToDoubleHours", "::absl::ToInt64Hours")}, + {DurationScale::Minutes, std::make_pair("::absl::ToDoubleMinutes", + "::absl::ToInt64Minutes")}, + {DurationScale::Seconds, std::make_pair("::absl::ToDoubleSeconds", + "::absl::ToInt64Seconds")}, + {DurationScale::Milliseconds, + std::make_pair("::absl::ToDoubleMilliseconds", + "::absl::ToInt64Milliseconds")}, + {DurationScale::Microseconds, + std::make_pair("::absl::ToDoubleMicroseconds", + "::absl::ToInt64Microseconds")}, + {DurationScale::Nanoseconds, + std::make_pair("::absl::ToDoubleNanoseconds", + "::absl::ToInt64Nanoseconds")}}); + + // We know our map contains all the Scale values, so we can skip the + // nonexistence check. + auto InverseIter = InverseMap.find(Scale); + assert(InverseIter != InverseMap.end() && "Unexpected scale found"); + return InverseIter->second; +} + +/// If `Node` is a call to the inverse of `Scale`, return that inverse's +/// argument, otherwise None. +static llvm::Optional<std::string> +maybeRewriteInverseDurationCall(const MatchFinder::MatchResult &Result, + DurationScale Scale, const Expr &Node) { + const std::pair<std::string, std::string> &InverseFunctions = + getInverseForScale(Scale); + if (const Expr *MaybeCallArg = selectFirst<const Expr>( + "e", match(callExpr(callee(functionDecl( + hasAnyName(InverseFunctions.first.c_str(), + InverseFunctions.second.c_str()))), + hasArgument(0, expr().bind("e"))), + Node, *Result.Context))) { + return tooling::fixit::getText(*MaybeCallArg, *Result.Context).str(); + } + + return llvm::None; +} + +/// Assuming `Node` has type `double` or `int` representing a time interval of +/// `Scale`, return the expression to make it a suitable `Duration`. +static llvm::Optional<std::string> rewriteExprFromNumberToDuration( + const ast_matchers::MatchFinder::MatchResult &Result, DurationScale Scale, + const Expr *Node) { + const Expr &RootNode = *Node->IgnoreParenImpCasts(); + + if (RootNode.getBeginLoc().isMacroID()) + return llvm::None; + + // First check to see if we can undo a complimentary function call. + if (llvm::Optional<std::string> MaybeRewrite = + maybeRewriteInverseDurationCall(Result, Scale, RootNode)) + return *MaybeRewrite; + + if (IsLiteralZero(Result, RootNode)) + return std::string("absl::ZeroDuration()"); + + return (llvm::Twine(getFactoryForScale(Scale)) + "(" + + simplifyDurationFactoryArg(Result, RootNode) + ")") + .str(); +} + +void DurationComparisonCheck::registerMatchers(MatchFinder *Finder) { + auto Matcher = + binaryOperator(anyOf(hasOperatorName(">"), hasOperatorName(">="), + hasOperatorName("=="), hasOperatorName("<="), + hasOperatorName("<")), + hasEitherOperand(ignoringImpCasts(callExpr( + callee(functionDecl(DurationConversionFunction()) + .bind("function_decl")))))) + .bind("binop"); + + Finder->addMatcher(Matcher, this); +} + +void DurationComparisonCheck::check(const MatchFinder::MatchResult &Result) { + const auto *Binop = Result.Nodes.getNodeAs<BinaryOperator>("binop"); + + // Don't try to replace things inside of macro definitions. + if (Binop->getExprLoc().isMacroID()) + return; + + llvm::Optional<DurationScale> Scale = getScaleForInverse( + Result.Nodes.getNodeAs<FunctionDecl>("function_decl")->getName()); + if (!Scale) + 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. + llvm::Optional<std::string> LhsReplacement = + rewriteExprFromNumberToDuration(Result, *Scale, Binop->getLHS()); + llvm::Optional<std::string> RhsReplacement = + rewriteExprFromNumberToDuration(Result, *Scale, Binop->getRHS()); + + if (!(LhsReplacement && RhsReplacement)) + return; + + diag(Binop->getBeginLoc(), "perform comparison in the duration domain") + << FixItHint::CreateReplacement(Binop->getSourceRange(), + (llvm::Twine(*LhsReplacement) + " " + + Binop->getOpcodeStr() + " " + + *RhsReplacement) + .str()); +} + +} // namespace abseil +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/abseil/DurationComparisonCheck.h b/clang-tools-extra/clang-tidy/abseil/DurationComparisonCheck.h new file mode 100644 index 00000000000..2d82f8ee73e --- /dev/null +++ b/clang-tools-extra/clang-tidy/abseil/DurationComparisonCheck.h @@ -0,0 +1,36 @@ +//===--- DurationComparisonCheck.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_DURATIONCOMPARISONCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_DURATIONCOMPARISONCHECK_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace abseil { + +/// Prefer comparison in the absl::Duration domain instead of the numeric +/// domain. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/abseil-duration-comparison.html +class DurationComparisonCheck : public ClangTidyCheck { +public: + DurationComparisonCheck(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_DURATIONCOMPARISONCHECK_H diff --git a/clang-tools-extra/clang-tidy/abseil/DurationFactoryFloatCheck.cpp b/clang-tools-extra/clang-tidy/abseil/DurationFactoryFloatCheck.cpp index 80ecf8a812e..06765144539 100644 --- a/clang-tools-extra/clang-tidy/abseil/DurationFactoryFloatCheck.cpp +++ b/clang-tools-extra/clang-tidy/abseil/DurationFactoryFloatCheck.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "DurationFactoryFloatCheck.h" +#include "DurationRewriter.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Tooling/FixIt.h" @@ -18,19 +19,6 @@ namespace clang { namespace tidy { namespace abseil { -// Returns an integer if the fractional part of a `FloatingLiteral` is `0`. -static llvm::Optional<llvm::APSInt> -truncateIfIntegral(const FloatingLiteral &FloatLiteral) { - double Value = FloatLiteral.getValueAsApproximateDouble(); - if (std::fmod(Value, 1) == 0) { - if (Value >= static_cast<double>(1u << 31)) - return llvm::None; - - return llvm::APSInt::get(static_cast<int64_t>(Value)); - } - return llvm::None; -} - // Returns `true` if `Range` is inside a macro definition. static bool InsideMacroDefinition(const MatchFinder::MatchResult &Result, SourceRange Range) { @@ -42,21 +30,14 @@ static bool InsideMacroDefinition(const MatchFinder::MatchResult &Result, void DurationFactoryFloatCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( - callExpr( - callee(functionDecl(hasAnyName( - "absl::Nanoseconds", "absl::Microseconds", "absl::Milliseconds", - "absl::Seconds", "absl::Minutes", "absl::Hours"))), - hasArgument(0, - anyOf(cxxStaticCastExpr( - hasDestinationType(realFloatingPointType()), - hasSourceExpression(expr().bind("cast_arg"))), - cStyleCastExpr( - hasDestinationType(realFloatingPointType()), - hasSourceExpression(expr().bind("cast_arg"))), - cxxFunctionalCastExpr( - hasDestinationType(realFloatingPointType()), - hasSourceExpression(expr().bind("cast_arg"))), - floatLiteral().bind("float_literal")))) + callExpr(callee(functionDecl(DurationFactoryFunction())), + hasArgument(0, anyOf(cxxStaticCastExpr(hasDestinationType( + realFloatingPointType())), + cStyleCastExpr(hasDestinationType( + realFloatingPointType())), + cxxFunctionalCastExpr(hasDestinationType( + realFloatingPointType())), + floatLiteral()))) .bind("call"), this); } @@ -73,31 +54,16 @@ void DurationFactoryFloatCheck::check(const MatchFinder::MatchResult &Result) { if (Arg->getBeginLoc().isMacroID()) return; - // Check for casts to `float` or `double`. - if (const auto *MaybeCastArg = Result.Nodes.getNodeAs<Expr>("cast_arg")) { + llvm::Optional<std::string> SimpleArg = stripFloatCast(Result, *Arg); + if (!SimpleArg) + SimpleArg = stripFloatLiteralFraction(Result, *Arg); + + if (SimpleArg) { diag(MatchedCall->getBeginLoc(), (llvm::Twine("use the integer version of absl::") + MatchedCall->getDirectCallee()->getName()) .str()) - << FixItHint::CreateReplacement( - Arg->getSourceRange(), - tooling::fixit::getText(*MaybeCastArg, *Result.Context)); - return; - } - - // Check for floats without fractional components. - if (const auto *LitFloat = - Result.Nodes.getNodeAs<FloatingLiteral>("float_literal")) { - // Attempt to simplify a `Duration` factory call with a literal argument. - if (llvm::Optional<llvm::APSInt> IntValue = truncateIfIntegral(*LitFloat)) { - diag(MatchedCall->getBeginLoc(), - (llvm::Twine("use the integer version of absl::") + - MatchedCall->getDirectCallee()->getName()) - .str()) - << FixItHint::CreateReplacement(LitFloat->getSourceRange(), - IntValue->toString(/*radix=*/10)); - return; - } + << FixItHint::CreateReplacement(Arg->getSourceRange(), *SimpleArg); } } diff --git a/clang-tools-extra/clang-tidy/abseil/DurationFactoryScaleCheck.cpp b/clang-tools-extra/clang-tidy/abseil/DurationFactoryScaleCheck.cpp index 83878e4e6da..078f88cff44 100644 --- a/clang-tools-extra/clang-tidy/abseil/DurationFactoryScaleCheck.cpp +++ b/clang-tools-extra/clang-tidy/abseil/DurationFactoryScaleCheck.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "DurationFactoryScaleCheck.h" +#include "DurationRewriter.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Tooling/FixIt.h" @@ -18,20 +19,6 @@ namespace clang { namespace tidy { namespace abseil { -namespace { - -// Potential scales of our inputs. -enum class DurationScale { - Hours, - Minutes, - Seconds, - Milliseconds, - Microseconds, - Nanoseconds, -}; - -} // namespace - // Given the name of a duration factory function, return the appropriate // `DurationScale` for that factory. If no factory can be found for // `FactoryName`, return `None`. @@ -129,39 +116,14 @@ static llvm::Optional<DurationScale> GetNewScale(DurationScale OldScale, return llvm::None; } -// Given a `Scale`, return the appropriate factory function call for -// constructing a `Duration` for that scale. -static llvm::StringRef GetFactoryForScale(DurationScale Scale) { - switch (Scale) { - case DurationScale::Hours: - return "absl::Hours"; - case DurationScale::Minutes: - return "absl::Minutes"; - case DurationScale::Seconds: - return "absl::Seconds"; - case DurationScale::Milliseconds: - return "absl::Milliseconds"; - case DurationScale::Microseconds: - return "absl::Microseconds"; - case DurationScale::Nanoseconds: - return "absl::Nanoseconds"; - } - llvm_unreachable("unknown scaling factor"); -} - void DurationFactoryScaleCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( callExpr( - callee(functionDecl( - hasAnyName("::absl::Nanoseconds", "::absl::Microseconds", - "::absl::Milliseconds", "::absl::Seconds", - "::absl::Minutes", "::absl::Hours")) - .bind("call_decl")), + callee(functionDecl(DurationFactoryFunction()).bind("call_decl")), hasArgument( 0, ignoringImpCasts(anyOf( - integerLiteral(equals(0)).bind("zero"), - floatLiteral(equals(0.0)).bind("zero"), + integerLiteral(equals(0)), floatLiteral(equals(0.0)), binaryOperator(hasOperatorName("*"), hasEitherOperand(ignoringImpCasts( anyOf(integerLiteral(), floatLiteral())))) @@ -185,7 +147,7 @@ void DurationFactoryScaleCheck::check(const MatchFinder::MatchResult &Result) { return; // We first handle the cases of literal zero (both float and integer). - if (Result.Nodes.getNodeAs<Stmt>("zero")) { + if (IsLiteralZero(Result, *Arg)) { diag(Call->getBeginLoc(), "use ZeroDuration() for zero-length time intervals") << FixItHint::CreateReplacement(Call->getSourceRange(), @@ -244,7 +206,7 @@ void DurationFactoryScaleCheck::check(const MatchFinder::MatchResult &Result) { diag(Call->getBeginLoc(), "internal duration scaling can be removed") << FixItHint::CreateReplacement( Call->getSourceRange(), - (llvm::Twine(GetFactoryForScale(*NewScale)) + "(" + + (llvm::Twine(getFactoryForScale(*NewScale)) + "(" + tooling::fixit::getText(*Remainder, *Result.Context) + ")") .str()); } @@ -257,7 +219,7 @@ void DurationFactoryScaleCheck::check(const MatchFinder::MatchResult &Result) { diag(Call->getBeginLoc(), "internal duration scaling can be removed") << FixItHint::CreateReplacement( Call->getSourceRange(), - (llvm::Twine(GetFactoryForScale(*NewScale)) + "(" + + (llvm::Twine(getFactoryForScale(*NewScale)) + "(" + tooling::fixit::getText(*Remainder, *Result.Context) + ")") .str()); } diff --git a/clang-tools-extra/clang-tidy/abseil/DurationRewriter.cpp b/clang-tools-extra/clang-tidy/abseil/DurationRewriter.cpp new file mode 100644 index 00000000000..b8a0f535f34 --- /dev/null +++ b/clang-tools-extra/clang-tidy/abseil/DurationRewriter.cpp @@ -0,0 +1,109 @@ +//===--- DurationRewriter.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 "DurationRewriter.h" +#include "clang/Tooling/FixIt.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace abseil { + +/// Returns an integer if the fractional part of a `FloatingLiteral` is `0`. +static llvm::Optional<llvm::APSInt> +truncateIfIntegral(const FloatingLiteral &FloatLiteral) { + double Value = FloatLiteral.getValueAsApproximateDouble(); + if (std::fmod(Value, 1) == 0) { + if (Value >= static_cast<double>(1u << 31)) + return llvm::None; + + return llvm::APSInt::get(static_cast<int64_t>(Value)); + } + return llvm::None; +} + +/// Returns the factory function name for a given `Scale`. +llvm::StringRef getFactoryForScale(DurationScale Scale) { + switch (Scale) { + case DurationScale::Hours: + return "absl::Hours"; + case DurationScale::Minutes: + return "absl::Minutes"; + case DurationScale::Seconds: + return "absl::Seconds"; + case DurationScale::Milliseconds: + return "absl::Milliseconds"; + case DurationScale::Microseconds: + return "absl::Microseconds"; + case DurationScale::Nanoseconds: + return "absl::Nanoseconds"; + } + llvm_unreachable("unknown scaling factor"); +} + +/// Returns `true` if `Node` is a value which evaluates to a literal `0`. +bool IsLiteralZero(const MatchFinder::MatchResult &Result, const Expr &Node) { + return selectFirst<const clang::Expr>( + "val", + match(expr(ignoringImpCasts(anyOf(integerLiteral(equals(0)), + floatLiteral(equals(0.0))))) + .bind("val"), + Node, *Result.Context)) != nullptr; +} + +llvm::Optional<std::string> +stripFloatCast(const ast_matchers::MatchFinder::MatchResult &Result, + const Expr &Node) { + if (const Expr *MaybeCastArg = selectFirst<const Expr>( + "cast_arg", + match(expr(anyOf(cxxStaticCastExpr( + hasDestinationType(realFloatingPointType()), + hasSourceExpression(expr().bind("cast_arg"))), + cStyleCastExpr( + hasDestinationType(realFloatingPointType()), + hasSourceExpression(expr().bind("cast_arg"))), + cxxFunctionalCastExpr( + hasDestinationType(realFloatingPointType()), + hasSourceExpression(expr().bind("cast_arg"))))), + Node, *Result.Context))) + return tooling::fixit::getText(*MaybeCastArg, *Result.Context).str(); + + return llvm::None; +} + +llvm::Optional<std::string> +stripFloatLiteralFraction(const MatchFinder::MatchResult &Result, + const Expr &Node) { + if (const auto *LitFloat = llvm::dyn_cast<FloatingLiteral>(&Node)) + // Attempt to simplify a `Duration` factory call with a literal argument. + if (llvm::Optional<llvm::APSInt> IntValue = truncateIfIntegral(*LitFloat)) + return IntValue->toString(/*radix=*/10); + + return llvm::None; +} + +std::string simplifyDurationFactoryArg(const MatchFinder::MatchResult &Result, + const Expr &Node) { + // Check for an explicit cast to `float` or `double`. + if (llvm::Optional<std::string> MaybeArg = stripFloatCast(Result, Node)) + return *MaybeArg; + + // Check for floats without fractional components. + if (llvm::Optional<std::string> MaybeArg = + stripFloatLiteralFraction(Result, Node)) + return *MaybeArg; + + // We couldn't simplify any further, so return the argument text. + return tooling::fixit::getText(Node, *Result.Context).str(); +} + +} // namespace abseil +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/abseil/DurationRewriter.h b/clang-tools-extra/clang-tidy/abseil/DurationRewriter.h new file mode 100644 index 00000000000..f5d2e0df60a --- /dev/null +++ b/clang-tools-extra/clang-tidy/abseil/DurationRewriter.h @@ -0,0 +1,86 @@ +//===--- DurationRewriter.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_DURATIONREWRITER_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_DURATIONREWRITER_H + +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include <cinttypes> + +namespace clang { +namespace tidy { +namespace abseil { + +/// Duration factory and conversion scales +enum class DurationScale : std::int8_t { + Hours, + Minutes, + Seconds, + Milliseconds, + Microseconds, + Nanoseconds, +}; + +/// Given a `Scale`, return the appropriate factory function call for +/// constructing a `Duration` for that scale. +llvm::StringRef getFactoryForScale(DurationScale Scale); + +// Determine if `Node` represents a literal floating point or integral zero. +bool IsLiteralZero(const ast_matchers::MatchFinder::MatchResult &Result, + const Expr &Node); + +/// Possibly strip a floating point cast expression. +/// +/// If `Node` represents an explicit cast to a floating point type, return +/// the textual context of the cast argument, otherwise `None`. +llvm::Optional<std::string> +stripFloatCast(const ast_matchers::MatchFinder::MatchResult &Result, + const Expr &Node); + +/// Possibly remove the fractional part of a floating point literal. +/// +/// If `Node` represents a floating point literal with a zero fractional part, +/// return the textual context of the integral part, otherwise `None`. +llvm::Optional<std::string> +stripFloatLiteralFraction(const ast_matchers::MatchFinder::MatchResult &Result, + const Expr &Node); + +/// Possibly further simplify a duration factory function's argument, without +/// changing the scale of the factory function. Return that simplification or +/// the text of the argument if no simplification is possible. +std::string +simplifyDurationFactoryArg(const ast_matchers::MatchFinder::MatchResult &Result, + const Expr &Node); + +AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher<FunctionDecl>, + DurationConversionFunction) { + using namespace clang::ast_matchers; + return functionDecl( + hasAnyName("::absl::ToDoubleHours", "::absl::ToDoubleMinutes", + "::absl::ToDoubleSeconds", "::absl::ToDoubleMilliseconds", + "::absl::ToDoubleMicroseconds", "::absl::ToDoubleNanoseconds", + "::absl::ToInt64Hours", "::absl::ToInt64Minutes", + "::absl::ToInt64Seconds", "::absl::ToInt64Milliseconds", + "::absl::ToInt64Microseconds", "::absl::ToInt64Nanoseconds")); +} + +AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher<FunctionDecl>, + DurationFactoryFunction) { + using namespace clang::ast_matchers; + return functionDecl(hasAnyName("::absl::Nanoseconds", "::absl::Microseconds", + "::absl::Milliseconds", "::absl::Seconds", + "::absl::Minutes", "::absl::Hours")); +} + +} // namespace abseil +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_DURATIONCOMPARISONCHECK_H |