forked from OSchip/llvm-project
				
			[clang-tidy] Add the abseil-time-subtraction check
Differential Revision: https://reviews.llvm.org/D58137 llvm-svn: 355024
This commit is contained in:
		
							parent
							
								
									ac552f77f4
								
							
						
					
					
						commit
						c526e02668
					
				| 
						 | 
					@ -23,6 +23,7 @@
 | 
				
			||||||
#include "RedundantStrcatCallsCheck.h"
 | 
					#include "RedundantStrcatCallsCheck.h"
 | 
				
			||||||
#include "StringFindStartswithCheck.h"
 | 
					#include "StringFindStartswithCheck.h"
 | 
				
			||||||
#include "StrCatAppendCheck.h"
 | 
					#include "StrCatAppendCheck.h"
 | 
				
			||||||
 | 
					#include "TimeSubtractionCheck.h"
 | 
				
			||||||
#include "UpgradeDurationConversionsCheck.h"
 | 
					#include "UpgradeDurationConversionsCheck.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace clang {
 | 
					namespace clang {
 | 
				
			||||||
| 
						 | 
					@ -59,6 +60,8 @@ public:
 | 
				
			||||||
        "abseil-str-cat-append");
 | 
					        "abseil-str-cat-append");
 | 
				
			||||||
    CheckFactories.registerCheck<StringFindStartswithCheck>(
 | 
					    CheckFactories.registerCheck<StringFindStartswithCheck>(
 | 
				
			||||||
        "abseil-string-find-startswith");
 | 
					        "abseil-string-find-startswith");
 | 
				
			||||||
 | 
					    CheckFactories.registerCheck<TimeSubtractionCheck>(
 | 
				
			||||||
 | 
					        "abseil-time-subtraction");
 | 
				
			||||||
    CheckFactories.registerCheck<UpgradeDurationConversionsCheck>(
 | 
					    CheckFactories.registerCheck<UpgradeDurationConversionsCheck>(
 | 
				
			||||||
        "abseil-upgrade-duration-conversions");
 | 
					        "abseil-upgrade-duration-conversions");
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,6 +17,7 @@ add_clang_library(clangTidyAbseilModule
 | 
				
			||||||
  RedundantStrcatCallsCheck.cpp
 | 
					  RedundantStrcatCallsCheck.cpp
 | 
				
			||||||
  StrCatAppendCheck.cpp
 | 
					  StrCatAppendCheck.cpp
 | 
				
			||||||
  StringFindStartswithCheck.cpp
 | 
					  StringFindStartswithCheck.cpp
 | 
				
			||||||
 | 
					  TimeSubtractionCheck.cpp
 | 
				
			||||||
  UpgradeDurationConversionsCheck.cpp
 | 
					  UpgradeDurationConversionsCheck.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  LINK_LIBS
 | 
					  LINK_LIBS
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -84,6 +84,22 @@ rewriteInverseDurationCall(const MatchFinder::MatchResult &Result,
 | 
				
			||||||
  return llvm::None;
 | 
					  return llvm::None;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// If `Node` is a call to the inverse of `Scale`, return that inverse's
 | 
				
			||||||
 | 
					/// argument, otherwise None.
 | 
				
			||||||
 | 
					static llvm::Optional<std::string>
 | 
				
			||||||
 | 
					rewriteInverseTimeCall(const MatchFinder::MatchResult &Result,
 | 
				
			||||||
 | 
					                       DurationScale Scale, const Expr &Node) {
 | 
				
			||||||
 | 
					  llvm::StringRef InverseFunction = getTimeInverseForScale(Scale);
 | 
				
			||||||
 | 
					  if (const auto *MaybeCallArg = selectFirst<const Expr>(
 | 
				
			||||||
 | 
					          "e", match(callExpr(callee(functionDecl(hasName(InverseFunction))),
 | 
				
			||||||
 | 
					                              hasArgument(0, expr().bind("e"))),
 | 
				
			||||||
 | 
					                     Node, *Result.Context))) {
 | 
				
			||||||
 | 
					    return tooling::fixit::getText(*MaybeCallArg, *Result.Context).str();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return llvm::None;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Returns the factory function name for a given `Scale`.
 | 
					/// Returns the factory function name for a given `Scale`.
 | 
				
			||||||
llvm::StringRef getDurationFactoryForScale(DurationScale Scale) {
 | 
					llvm::StringRef getDurationFactoryForScale(DurationScale Scale) {
 | 
				
			||||||
  switch (Scale) {
 | 
					  switch (Scale) {
 | 
				
			||||||
| 
						 | 
					@ -103,6 +119,24 @@ llvm::StringRef getDurationFactoryForScale(DurationScale Scale) {
 | 
				
			||||||
  llvm_unreachable("unknown scaling factor");
 | 
					  llvm_unreachable("unknown scaling factor");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					llvm::StringRef getTimeFactoryForScale(DurationScale Scale) {
 | 
				
			||||||
 | 
					  switch (Scale) {
 | 
				
			||||||
 | 
					  case DurationScale::Hours:
 | 
				
			||||||
 | 
					    return "absl::FromUnixHours";
 | 
				
			||||||
 | 
					  case DurationScale::Minutes:
 | 
				
			||||||
 | 
					    return "absl::FromUnixMinutes";
 | 
				
			||||||
 | 
					  case DurationScale::Seconds:
 | 
				
			||||||
 | 
					    return "absl::FromUnixSeconds";
 | 
				
			||||||
 | 
					  case DurationScale::Milliseconds:
 | 
				
			||||||
 | 
					    return "absl::FromUnixMillis";
 | 
				
			||||||
 | 
					  case DurationScale::Microseconds:
 | 
				
			||||||
 | 
					    return "absl::FromUnixMicros";
 | 
				
			||||||
 | 
					  case DurationScale::Nanoseconds:
 | 
				
			||||||
 | 
					    return "absl::FromUnixNanos";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  llvm_unreachable("unknown scaling factor");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Returns the Time factory function name for a given `Scale`.
 | 
					/// Returns the Time factory function name for a given `Scale`.
 | 
				
			||||||
llvm::StringRef getTimeInverseForScale(DurationScale scale) {
 | 
					llvm::StringRef getTimeInverseForScale(DurationScale scale) {
 | 
				
			||||||
  switch (scale) {
 | 
					  switch (scale) {
 | 
				
			||||||
| 
						 | 
					@ -250,6 +284,24 @@ std::string rewriteExprFromNumberToDuration(
 | 
				
			||||||
      .str();
 | 
					      .str();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::string rewriteExprFromNumberToTime(
 | 
				
			||||||
 | 
					    const ast_matchers::MatchFinder::MatchResult &Result, DurationScale Scale,
 | 
				
			||||||
 | 
					    const Expr *Node) {
 | 
				
			||||||
 | 
					  const Expr &RootNode = *Node->IgnoreParenImpCasts();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // First check to see if we can undo a complimentary function call.
 | 
				
			||||||
 | 
					  if (llvm::Optional<std::string> MaybeRewrite =
 | 
				
			||||||
 | 
					          rewriteInverseTimeCall(Result, Scale, RootNode))
 | 
				
			||||||
 | 
					    return *MaybeRewrite;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (IsLiteralZero(Result, RootNode))
 | 
				
			||||||
 | 
					    return std::string("absl::UnixEpoch()");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (llvm::Twine(getTimeFactoryForScale(Scale)) + "(" +
 | 
				
			||||||
 | 
					          tooling::fixit::getText(RootNode, *Result.Context) + ")")
 | 
				
			||||||
 | 
					      .str();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool isNotInMacro(const MatchFinder::MatchResult &Result, const Expr *E) {
 | 
					bool isNotInMacro(const MatchFinder::MatchResult &Result, const Expr *E) {
 | 
				
			||||||
  if (!E->getBeginLoc().isMacroID())
 | 
					  if (!E->getBeginLoc().isMacroID())
 | 
				
			||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,6 +31,10 @@ enum class DurationScale : std::uint8_t {
 | 
				
			||||||
/// constructing a `Duration` for that scale.
 | 
					/// constructing a `Duration` for that scale.
 | 
				
			||||||
llvm::StringRef getDurationFactoryForScale(DurationScale Scale);
 | 
					llvm::StringRef getDurationFactoryForScale(DurationScale Scale);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Given a 'Scale', return the appropriate factory function call for
 | 
				
			||||||
 | 
					/// constructing a `Time` for that scale.
 | 
				
			||||||
 | 
					llvm::StringRef getTimeFactoryForScale(DurationScale scale);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Determine if `Node` represents a literal floating point or integral zero.
 | 
					// Determine if `Node` represents a literal floating point or integral zero.
 | 
				
			||||||
bool IsLiteralZero(const ast_matchers::MatchFinder::MatchResult &Result,
 | 
					bool IsLiteralZero(const ast_matchers::MatchFinder::MatchResult &Result,
 | 
				
			||||||
                   const Expr &Node);
 | 
					                   const Expr &Node);
 | 
				
			||||||
| 
						 | 
					@ -81,6 +85,12 @@ std::string rewriteExprFromNumberToDuration(
 | 
				
			||||||
    const ast_matchers::MatchFinder::MatchResult &Result, DurationScale Scale,
 | 
					    const ast_matchers::MatchFinder::MatchResult &Result, DurationScale Scale,
 | 
				
			||||||
    const Expr *Node);
 | 
					    const Expr *Node);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Assuming `Node` has a type `int` representing a time instant of `Scale`
 | 
				
			||||||
 | 
					/// since The Epoch, return the expression to make it a suitable `Time`.
 | 
				
			||||||
 | 
					std::string rewriteExprFromNumberToTime(
 | 
				
			||||||
 | 
					    const ast_matchers::MatchFinder::MatchResult &Result, DurationScale Scale,
 | 
				
			||||||
 | 
					    const Expr *Node);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Return `true` if `E` is a either: not a macro at all; or an argument to
 | 
					/// Return `true` if `E` is a either: not a macro at all; or an argument to
 | 
				
			||||||
/// one.  In the both cases, we often want to do the transformation.
 | 
					/// one.  In the both cases, we often want to do the transformation.
 | 
				
			||||||
bool isNotInMacro(const ast_matchers::MatchFinder::MatchResult &Result,
 | 
					bool isNotInMacro(const ast_matchers::MatchFinder::MatchResult &Result,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,181 @@
 | 
				
			||||||
 | 
					//===--- TimeSubtractionCheck.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 "TimeSubtractionCheck.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 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Returns `true` if `Range` is inside a macro definition.
 | 
				
			||||||
 | 
					static bool InsideMacroDefinition(const MatchFinder::MatchResult &Result,
 | 
				
			||||||
 | 
					                                  SourceRange Range) {
 | 
				
			||||||
 | 
					  return !clang::Lexer::makeFileCharRange(
 | 
				
			||||||
 | 
					              clang::CharSourceRange::getCharRange(Range),
 | 
				
			||||||
 | 
					              *Result.SourceManager, Result.Context->getLangOpts())
 | 
				
			||||||
 | 
					              .isValid();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool isConstructorAssignment(const MatchFinder::MatchResult &Result,
 | 
				
			||||||
 | 
					                                    const Expr *Node) {
 | 
				
			||||||
 | 
					  return selectFirst<const Expr>(
 | 
				
			||||||
 | 
					             "e", match(expr(hasParent(materializeTemporaryExpr(hasParent(
 | 
				
			||||||
 | 
					                                 cxxConstructExpr(hasParent(exprWithCleanups(
 | 
				
			||||||
 | 
					                                     hasParent(varDecl()))))))))
 | 
				
			||||||
 | 
					                            .bind("e"),
 | 
				
			||||||
 | 
					                        *Node, *Result.Context)) != nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool isArgument(const MatchFinder::MatchResult &Result,
 | 
				
			||||||
 | 
					                       const Expr *Node) {
 | 
				
			||||||
 | 
					  return selectFirst<const Expr>(
 | 
				
			||||||
 | 
					             "e",
 | 
				
			||||||
 | 
					             match(expr(hasParent(
 | 
				
			||||||
 | 
					                            materializeTemporaryExpr(hasParent(cxxConstructExpr(
 | 
				
			||||||
 | 
					                                hasParent(callExpr()),
 | 
				
			||||||
 | 
					                                unless(hasParent(cxxOperatorCallExpr())))))))
 | 
				
			||||||
 | 
					                       .bind("e"),
 | 
				
			||||||
 | 
					                   *Node, *Result.Context)) != nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool isReturn(const MatchFinder::MatchResult &Result, const Expr *Node) {
 | 
				
			||||||
 | 
					  return selectFirst<const Expr>(
 | 
				
			||||||
 | 
					             "e", match(expr(hasParent(materializeTemporaryExpr(hasParent(
 | 
				
			||||||
 | 
					                                 cxxConstructExpr(hasParent(exprWithCleanups(
 | 
				
			||||||
 | 
					                                     hasParent(returnStmt()))))))))
 | 
				
			||||||
 | 
					                            .bind("e"),
 | 
				
			||||||
 | 
					                        *Node, *Result.Context)) != nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool parensRequired(const MatchFinder::MatchResult &Result,
 | 
				
			||||||
 | 
					                           const Expr *Node) {
 | 
				
			||||||
 | 
					  // TODO: Figure out any more contexts in which we can omit the surrounding
 | 
				
			||||||
 | 
					  // parentheses.
 | 
				
			||||||
 | 
					  return !(isConstructorAssignment(Result, Node) || isArgument(Result, Node) ||
 | 
				
			||||||
 | 
					           isReturn(Result, Node));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void TimeSubtractionCheck::emitDiagnostic(const Expr *Node,
 | 
				
			||||||
 | 
					                                          llvm::StringRef Replacement) {
 | 
				
			||||||
 | 
					  diag(Node->getBeginLoc(), "perform subtraction in the time domain")
 | 
				
			||||||
 | 
					      << FixItHint::CreateReplacement(Node->getSourceRange(), Replacement);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void TimeSubtractionCheck::registerMatchers(MatchFinder *Finder) {
 | 
				
			||||||
 | 
					  for (auto ScaleName :
 | 
				
			||||||
 | 
					       {"Hours", "Minutes", "Seconds", "Millis", "Micros", "Nanos"}) {
 | 
				
			||||||
 | 
					    std::string TimeInverse = (llvm::Twine("ToUnix") + ScaleName).str();
 | 
				
			||||||
 | 
					    llvm::Optional<DurationScale> Scale = getScaleForTimeInverse(TimeInverse);
 | 
				
			||||||
 | 
					    assert(Scale && "Unknow scale encountered");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    auto TimeInverseMatcher = callExpr(callee(
 | 
				
			||||||
 | 
					        functionDecl(hasName((llvm::Twine("::absl::") + TimeInverse).str()))
 | 
				
			||||||
 | 
					            .bind("func_decl")));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Match the cases where we know that the result is a 'Duration' and the
 | 
				
			||||||
 | 
					    // first argument is a 'Time'. Just knowing the type of the first operand
 | 
				
			||||||
 | 
					    // is not sufficient, since the second operand could be either a 'Time' or
 | 
				
			||||||
 | 
					    // a 'Duration'. If we know the result is a 'Duration', we can then infer
 | 
				
			||||||
 | 
					    // that the second operand must be a 'Time'.
 | 
				
			||||||
 | 
					    auto CallMatcher =
 | 
				
			||||||
 | 
					        callExpr(
 | 
				
			||||||
 | 
					            callee(functionDecl(hasName(getDurationFactoryForScale(*Scale)))),
 | 
				
			||||||
 | 
					            hasArgument(0, binaryOperator(hasOperatorName("-"),
 | 
				
			||||||
 | 
					                                          hasLHS(TimeInverseMatcher))
 | 
				
			||||||
 | 
					                               .bind("binop")))
 | 
				
			||||||
 | 
					            .bind("outer_call");
 | 
				
			||||||
 | 
					    Finder->addMatcher(CallMatcher, this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Match cases where we know the second operand is a 'Time'. Since
 | 
				
			||||||
 | 
					    // subtracting a 'Time' from a 'Duration' is not defined, in these cases,
 | 
				
			||||||
 | 
					    // we always know the first operand is a 'Time' if the second is a 'Time'.
 | 
				
			||||||
 | 
					    auto OperandMatcher =
 | 
				
			||||||
 | 
					        binaryOperator(hasOperatorName("-"), hasRHS(TimeInverseMatcher))
 | 
				
			||||||
 | 
					            .bind("binop");
 | 
				
			||||||
 | 
					    Finder->addMatcher(OperandMatcher, this);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void TimeSubtractionCheck::check(const MatchFinder::MatchResult &Result) {
 | 
				
			||||||
 | 
					  const auto *BinOp = Result.Nodes.getNodeAs<BinaryOperator>("binop");
 | 
				
			||||||
 | 
					  std::string InverseName =
 | 
				
			||||||
 | 
					      Result.Nodes.getNodeAs<FunctionDecl>("func_decl")->getNameAsString();
 | 
				
			||||||
 | 
					  if (InsideMacroDefinition(Result, BinOp->getSourceRange()))
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  llvm::Optional<DurationScale> Scale = getScaleForTimeInverse(InverseName);
 | 
				
			||||||
 | 
					  if (!Scale)
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const auto *OuterCall = Result.Nodes.getNodeAs<CallExpr>("outer_call");
 | 
				
			||||||
 | 
					  if (OuterCall) {
 | 
				
			||||||
 | 
					    if (InsideMacroDefinition(Result, OuterCall->getSourceRange()))
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // We're working with the first case of matcher, and need to replace the
 | 
				
			||||||
 | 
					    // entire 'Duration' factory call. (Which also means being careful about
 | 
				
			||||||
 | 
					    // our order-of-operations and optionally putting in some parenthesis.
 | 
				
			||||||
 | 
					    bool NeedParens = parensRequired(Result, OuterCall);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    emitDiagnostic(
 | 
				
			||||||
 | 
					        OuterCall,
 | 
				
			||||||
 | 
					        (llvm::Twine(NeedParens ? "(" : "") +
 | 
				
			||||||
 | 
					         rewriteExprFromNumberToTime(Result, *Scale, BinOp->getLHS()) + " - " +
 | 
				
			||||||
 | 
					         rewriteExprFromNumberToTime(Result, *Scale, BinOp->getRHS()) +
 | 
				
			||||||
 | 
					         (NeedParens ? ")" : ""))
 | 
				
			||||||
 | 
					            .str());
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    // We're working with the second case of matcher, and either just need to
 | 
				
			||||||
 | 
					    // change the arguments, or perhaps remove an outer function call. In the
 | 
				
			||||||
 | 
					    // latter case (addressed first), we also need to worry about parenthesis.
 | 
				
			||||||
 | 
					    const auto *MaybeCallArg = selectFirst<const CallExpr>(
 | 
				
			||||||
 | 
					        "arg", match(expr(hasAncestor(
 | 
				
			||||||
 | 
					                         callExpr(callee(functionDecl(hasName(
 | 
				
			||||||
 | 
					                                      getDurationFactoryForScale(*Scale)))))
 | 
				
			||||||
 | 
					                             .bind("arg"))),
 | 
				
			||||||
 | 
					                     *BinOp, *Result.Context));
 | 
				
			||||||
 | 
					    if (MaybeCallArg && MaybeCallArg->getArg(0)->IgnoreImpCasts() == BinOp &&
 | 
				
			||||||
 | 
					        !InsideMacroDefinition(Result, MaybeCallArg->getSourceRange())) {
 | 
				
			||||||
 | 
					      // Handle the case where the matched expression is inside a call which
 | 
				
			||||||
 | 
					      // converts it from the inverse to a Duration.  In this case, we replace
 | 
				
			||||||
 | 
					      // the outer with just the subtraction expresison, which gives the right
 | 
				
			||||||
 | 
					      // type and scale, taking care again about parenthesis.
 | 
				
			||||||
 | 
					      bool NeedParens = parensRequired(Result, MaybeCallArg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      emitDiagnostic(
 | 
				
			||||||
 | 
					          MaybeCallArg,
 | 
				
			||||||
 | 
					          (llvm::Twine(NeedParens ? "(" : "") +
 | 
				
			||||||
 | 
					           rewriteExprFromNumberToTime(Result, *Scale, BinOp->getLHS()) +
 | 
				
			||||||
 | 
					           " - " +
 | 
				
			||||||
 | 
					           rewriteExprFromNumberToTime(Result, *Scale, BinOp->getRHS()) +
 | 
				
			||||||
 | 
					           (NeedParens ? ")" : ""))
 | 
				
			||||||
 | 
					              .str());
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      // In the last case, just convert the arguments and wrap the result in
 | 
				
			||||||
 | 
					      // the correct inverse function.
 | 
				
			||||||
 | 
					      emitDiagnostic(
 | 
				
			||||||
 | 
					          BinOp,
 | 
				
			||||||
 | 
					          (llvm::Twine(
 | 
				
			||||||
 | 
					               getDurationInverseForScale(*Scale).second.str().substr(2)) +
 | 
				
			||||||
 | 
					           "(" + rewriteExprFromNumberToTime(Result, *Scale, BinOp->getLHS()) +
 | 
				
			||||||
 | 
					           " - " +
 | 
				
			||||||
 | 
					           rewriteExprFromNumberToTime(Result, *Scale, BinOp->getRHS()) + ")")
 | 
				
			||||||
 | 
					              .str());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace abseil
 | 
				
			||||||
 | 
					} // namespace tidy
 | 
				
			||||||
 | 
					} // namespace clang
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,38 @@
 | 
				
			||||||
 | 
					//===--- TimeSubtractionCheck.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_TIMESUBTRACTIONCHECK_H
 | 
				
			||||||
 | 
					#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_TIMESUBTRACTIONCHECK_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../ClangTidy.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace clang {
 | 
				
			||||||
 | 
					namespace tidy {
 | 
				
			||||||
 | 
					namespace abseil {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Finds and fixes `absl::Time` subtraction expressions to do subtraction
 | 
				
			||||||
 | 
					/// in the time domain instead of the numeric domain.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// For the user-facing documentation see:
 | 
				
			||||||
 | 
					/// http://clang.llvm.org/extra/clang-tidy/checks/abseil-time-subtraction.html
 | 
				
			||||||
 | 
					class TimeSubtractionCheck : public ClangTidyCheck {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					  TimeSubtractionCheck(StringRef Name, ClangTidyContext *Context)
 | 
				
			||||||
 | 
					      : ClangTidyCheck(Name, Context) {}
 | 
				
			||||||
 | 
					  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
 | 
				
			||||||
 | 
					  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					  void emitDiagnostic(const Expr* Node, llvm::StringRef Replacement);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace abseil
 | 
				
			||||||
 | 
					} // namespace tidy
 | 
				
			||||||
 | 
					} // namespace clang
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_TIMESUBTRACTIONCHECK_H
 | 
				
			||||||
| 
						 | 
					@ -85,6 +85,12 @@ Improvements to clang-tidy
 | 
				
			||||||
  Finds and fixes cases where ``absl::Duration`` values are being converted to
 | 
					  Finds and fixes cases where ``absl::Duration`` values are being converted to
 | 
				
			||||||
  numeric types and back again.
 | 
					  numeric types and back again.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- New :doc:`abseil-time-subtraction
 | 
				
			||||||
 | 
					  <clang-tidy/checks/abseil-time-subtraction>` check.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Finds and fixes ``absl::Time`` subtraction expressions to do subtraction
 | 
				
			||||||
 | 
					  in the Time domain instead of the numeric domain.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- New :doc:`google-readability-avoid-underscore-in-googletest-name
 | 
					- New :doc:`google-readability-avoid-underscore-in-googletest-name
 | 
				
			||||||
  <clang-tidy/checks/google-readability-avoid-underscore-in-googletest-name>`
 | 
					  <clang-tidy/checks/google-readability-avoid-underscore-in-googletest-name>`
 | 
				
			||||||
  check.
 | 
					  check.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,37 @@
 | 
				
			||||||
 | 
					.. title:: clang-tidy - abseil-time-subtraction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					abseil-time-subtraction
 | 
				
			||||||
 | 
					=======================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Finds and fixes ``absl::Time`` subtraction expressions to do subtraction
 | 
				
			||||||
 | 
					in the Time domain instead of the numeric domain.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					There are two cases of Time subtraction in which deduce additional type
 | 
				
			||||||
 | 
					information:
 | 
				
			||||||
 | 
					 - When the result is an ``absl::Duration`` and the first argument is an
 | 
				
			||||||
 | 
					   ``absl::Time``.
 | 
				
			||||||
 | 
					 - When the second argument is a ``absl::Time``.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In the first case, we must know the result of the operation, since without that
 | 
				
			||||||
 | 
					the second operand could be either an ``absl::Time`` or an ``absl::Duration``.
 | 
				
			||||||
 | 
					In the second case, the first operand *must* be an ``absl::Time``, because
 | 
				
			||||||
 | 
					subtracting an ``absl::Time`` from an ``absl::Duration`` is not defined.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Examples:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. code-block:: c++
 | 
				
			||||||
 | 
					  int x;
 | 
				
			||||||
 | 
					  absl::Time t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Original - absl::Duration result and first operand is a absl::Time.
 | 
				
			||||||
 | 
					  absl::Duration d = absl::Seconds(absl::ToUnixSeconds(t) - x);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Suggestion - Perform subtraction in the Time domain instead.
 | 
				
			||||||
 | 
					  absl::Duration d = t - absl::FromUnixSeconds(x);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Original - Second operand is an absl::Time.
 | 
				
			||||||
 | 
					  int i = x - absl::ToUnixSeconds(t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Suggestion - Perform subtraction in the Time domain instead.
 | 
				
			||||||
 | 
					  int i = absl::ToInt64Seconds(absl::FromUnixSeconds(x) - t);
 | 
				
			||||||
| 
						 | 
					@ -18,6 +18,7 @@ Clang-Tidy Checks
 | 
				
			||||||
   abseil-redundant-strcat-calls
 | 
					   abseil-redundant-strcat-calls
 | 
				
			||||||
   abseil-str-cat-append
 | 
					   abseil-str-cat-append
 | 
				
			||||||
   abseil-string-find-startswith
 | 
					   abseil-string-find-startswith
 | 
				
			||||||
 | 
					   abseil-time-subtraction
 | 
				
			||||||
   abseil-upgrade-duration-conversions
 | 
					   abseil-upgrade-duration-conversions
 | 
				
			||||||
   android-cloexec-accept
 | 
					   android-cloexec-accept
 | 
				
			||||||
   android-cloexec-accept4
 | 
					   android-cloexec-accept4
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -70,6 +70,8 @@ Time FromUnixMillis(int64_t);
 | 
				
			||||||
Time FromUnixMicros(int64_t);
 | 
					Time FromUnixMicros(int64_t);
 | 
				
			||||||
Time FromUnixNanos(int64_t);
 | 
					Time FromUnixNanos(int64_t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Time Now();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Relational Operators
 | 
					// Relational Operators
 | 
				
			||||||
constexpr bool operator<(Duration lhs, Duration rhs);
 | 
					constexpr bool operator<(Duration lhs, Duration rhs);
 | 
				
			||||||
constexpr bool operator>(Duration lhs, Duration rhs);
 | 
					constexpr bool operator>(Duration lhs, Duration rhs);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,117 @@
 | 
				
			||||||
 | 
					// RUN: %check_clang_tidy %s abseil-time-subtraction %t -- -- -I%S/Inputs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "absl/time/time.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void g(absl::Duration d);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void f() {
 | 
				
			||||||
 | 
					  absl::Time t;
 | 
				
			||||||
 | 
					  int x, y;
 | 
				
			||||||
 | 
					  absl::Duration d;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  d = absl::Hours(absl::ToUnixHours(t) - x);
 | 
				
			||||||
 | 
					  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform subtraction in the time domain [abseil-time-subtraction]
 | 
				
			||||||
 | 
					  // CHECK-FIXES: d = (t - absl::FromUnixHours(x));
 | 
				
			||||||
 | 
					  d = absl::Minutes(absl::ToUnixMinutes(t) - x);
 | 
				
			||||||
 | 
					  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform subtraction in the time domain [abseil-time-subtraction]
 | 
				
			||||||
 | 
					  // CHECK-FIXES: d = (t - absl::FromUnixMinutes(x));
 | 
				
			||||||
 | 
					  d = absl::Seconds(absl::ToUnixSeconds(t) - x);
 | 
				
			||||||
 | 
					  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform subtraction in the time domain [abseil-time-subtraction]
 | 
				
			||||||
 | 
					  // CHECK-FIXES: d = (t - absl::FromUnixSeconds(x));
 | 
				
			||||||
 | 
					  d = absl::Milliseconds(absl::ToUnixMillis(t) - x);
 | 
				
			||||||
 | 
					  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform subtraction in the time domain [abseil-time-subtraction]
 | 
				
			||||||
 | 
					  // CHECK-FIXES: d = (t - absl::FromUnixMillis(x));
 | 
				
			||||||
 | 
					  d = absl::Microseconds(absl::ToUnixMicros(t) - x);
 | 
				
			||||||
 | 
					  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform subtraction in the time domain [abseil-time-subtraction]
 | 
				
			||||||
 | 
					  // CHECK-FIXES: d = (t - absl::FromUnixMicros(x));
 | 
				
			||||||
 | 
					  d = absl::Nanoseconds(absl::ToUnixNanos(t) - x);
 | 
				
			||||||
 | 
					  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform subtraction in the time domain [abseil-time-subtraction]
 | 
				
			||||||
 | 
					  // CHECK-FIXES: d = (t - absl::FromUnixNanos(x));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  y = x - absl::ToUnixHours(t);
 | 
				
			||||||
 | 
					  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform subtraction in the time domain [abseil-time-subtraction]
 | 
				
			||||||
 | 
					  // CHECK-FIXES: y = absl::ToInt64Hours(absl::FromUnixHours(x) - t);
 | 
				
			||||||
 | 
					  y = x - absl::ToUnixMinutes(t);
 | 
				
			||||||
 | 
					  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform subtraction in the time domain [abseil-time-subtraction]
 | 
				
			||||||
 | 
					  // CHECK-FIXES: y = absl::ToInt64Minutes(absl::FromUnixMinutes(x) - t);
 | 
				
			||||||
 | 
					  y = x - absl::ToUnixSeconds(t);
 | 
				
			||||||
 | 
					  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform subtraction in the time domain [abseil-time-subtraction]
 | 
				
			||||||
 | 
					  // CHECK-FIXES: y = absl::ToInt64Seconds(absl::FromUnixSeconds(x) - t);
 | 
				
			||||||
 | 
					  y = x - absl::ToUnixMillis(t);
 | 
				
			||||||
 | 
					  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform subtraction in the time domain [abseil-time-subtraction]
 | 
				
			||||||
 | 
					  // CHECK-FIXES: y = absl::ToInt64Milliseconds(absl::FromUnixMillis(x) - t);
 | 
				
			||||||
 | 
					  y = x - absl::ToUnixMicros(t);
 | 
				
			||||||
 | 
					  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform subtraction in the time domain [abseil-time-subtraction]
 | 
				
			||||||
 | 
					  // CHECK-FIXES: y = absl::ToInt64Microseconds(absl::FromUnixMicros(x) - t);
 | 
				
			||||||
 | 
					  y = x - absl::ToUnixNanos(t);
 | 
				
			||||||
 | 
					  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform subtraction in the time domain [abseil-time-subtraction]
 | 
				
			||||||
 | 
					  // CHECK-FIXES: y = absl::ToInt64Nanoseconds(absl::FromUnixNanos(x) - t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Check parenthesis placement
 | 
				
			||||||
 | 
					  d = 5 * absl::Seconds(absl::ToUnixSeconds(t) - x);
 | 
				
			||||||
 | 
					  // CHECK-MESSAGES: [[@LINE-1]]:11: warning: perform subtraction in the time domain [abseil-time-subtraction]
 | 
				
			||||||
 | 
					  // CHECK-FIXES: d = 5 * (t - absl::FromUnixSeconds(x));
 | 
				
			||||||
 | 
					  d = absl::Seconds(absl::ToUnixSeconds(t) - x) / 5;
 | 
				
			||||||
 | 
					  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform subtraction in the time domain [abseil-time-subtraction]
 | 
				
			||||||
 | 
					  // CHECK-FIXES: d = (t - absl::FromUnixSeconds(x)) / 5;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // No extra parens around arguments
 | 
				
			||||||
 | 
					  g(absl::Seconds(absl::ToUnixSeconds(t) - x));
 | 
				
			||||||
 | 
					  // CHECK-MESSAGES: [[@LINE-1]]:5: warning: perform subtraction in the time domain [abseil-time-subtraction]
 | 
				
			||||||
 | 
					  // CHECK-FIXES: g(t - absl::FromUnixSeconds(x));
 | 
				
			||||||
 | 
					  g(absl::Seconds(x - absl::ToUnixSeconds(t)));
 | 
				
			||||||
 | 
					  // CHECK-MESSAGES: [[@LINE-1]]:5: warning: perform subtraction in the time domain [abseil-time-subtraction]
 | 
				
			||||||
 | 
					  // CHECK-FIXES: g(absl::FromUnixSeconds(x) - t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // More complex subexpressions
 | 
				
			||||||
 | 
					  d = absl::Hours(absl::ToUnixHours(t) - 5 * x);
 | 
				
			||||||
 | 
					  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: perform subtraction in the time domain [abseil-time-subtraction]
 | 
				
			||||||
 | 
					  // CHECK-FIXES: d = (t - absl::FromUnixHours(5 * x));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // These should not trigger; they are likely bugs
 | 
				
			||||||
 | 
					  d = absl::Milliseconds(absl::ToUnixSeconds(t) - x);
 | 
				
			||||||
 | 
					  d = absl::Seconds(absl::ToUnixMicros(t) - x);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Various macro scenarios
 | 
				
			||||||
 | 
					#define SUB(z, t1) z - absl::ToUnixSeconds(t1)
 | 
				
			||||||
 | 
					  y = SUB(x, t);
 | 
				
			||||||
 | 
					#undef SUB
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MILLIS(t1) absl::ToUnixMillis(t1)
 | 
				
			||||||
 | 
					  y = x - MILLIS(t);
 | 
				
			||||||
 | 
					#undef MILLIS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HOURS(z) absl::Hours(z)
 | 
				
			||||||
 | 
					  d = HOURS(absl::ToUnixHours(t) - x);
 | 
				
			||||||
 | 
					#undef HOURS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // This should match the expression inside the macro invocation.
 | 
				
			||||||
 | 
					#define SECONDS(z) absl::Seconds(z)
 | 
				
			||||||
 | 
					  d = SECONDS(x - absl::ToUnixSeconds(t));
 | 
				
			||||||
 | 
					  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: perform subtraction in the time domain [abseil-time-subtraction]
 | 
				
			||||||
 | 
					  // CHECK-FIXES: SECONDS(absl::ToInt64Seconds(absl::FromUnixSeconds(x) - t))
 | 
				
			||||||
 | 
					#undef SECONDS
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<typename T>
 | 
				
			||||||
 | 
					void func(absl::Time t, T x) {
 | 
				
			||||||
 | 
					  absl::Duration d = absl::Seconds(absl::ToUnixSeconds(t) - x);
 | 
				
			||||||
 | 
					  // CHECK-MESSAGES: [[@LINE-1]]:22: warning: perform subtraction in the time domain [abseil-time-subtraction]
 | 
				
			||||||
 | 
					  // CHECK-FIXES: absl::Duration d = t - absl::FromUnixSeconds(x);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void g() {
 | 
				
			||||||
 | 
					  func(absl::Now(), 5);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					absl::Duration parens_in_return() {
 | 
				
			||||||
 | 
					  absl::Time t;
 | 
				
			||||||
 | 
					  int x;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return absl::Seconds(absl::ToUnixSeconds(t) - x);
 | 
				
			||||||
 | 
					  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: perform subtraction in the time domain [abseil-time-subtraction]
 | 
				
			||||||
 | 
					  // CHECK-FIXES: return t - absl::FromUnixSeconds(x);
 | 
				
			||||||
 | 
					  return absl::Seconds(x - absl::ToUnixSeconds(t));
 | 
				
			||||||
 | 
					  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: perform subtraction in the time domain [abseil-time-subtraction]
 | 
				
			||||||
 | 
					  // CHECK-FIXES: return absl::FromUnixSeconds(x) - t;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue