Mercurial > hg > CbC > CbC_llvm
view clang-tools-extra/clang-tidy/abseil/TimeSubtractionCheck.cpp @ 204:e348f3e5c8b2
ReadFromString worked.
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Sat, 05 Jun 2021 15:35:13 +0900 |
parents | 0572611fdcc8 |
children | 2e18cbf3894f |
line wrap: on
line source
//===--- 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) { // For C++14 and earlier there are elidable constructors that must be matched // in hasParent. The elidable constructors do not exist in C++17 and later and // therefore an additional check that does not match against the elidable // constructors are needed for this case. return selectFirst<const Expr>( "e", match(expr(anyOf( callExpr(hasParent(materializeTemporaryExpr(hasParent( cxxConstructExpr(hasParent(exprWithCleanups( hasParent(varDecl())))))))) .bind("e"), callExpr(hasParent(varDecl())).bind("e"))), *Node, *Result.Context)) != nullptr; } static bool isArgument(const MatchFinder::MatchResult &Result, const Expr *Node) { // For the same reason as in isConstructorAssignment two AST shapes need to be // matched here. return selectFirst<const Expr>( "e", match( expr(anyOf( expr(hasParent(materializeTemporaryExpr( hasParent(cxxConstructExpr( hasParent(callExpr()), unless(hasParent(cxxOperatorCallExpr()))))))) .bind("e"), expr(hasParent(callExpr()), unless(hasParent(cxxOperatorCallExpr()))) .bind("e"))), *Node, *Result.Context)) != nullptr; } static bool isReturn(const MatchFinder::MatchResult &Result, const Expr *Node) { // For the same reason as in isConstructorAssignment two AST shapes need to be // matched here. return selectFirst<const Expr>( "e", match(expr(anyOf( expr(hasParent(materializeTemporaryExpr(hasParent( cxxConstructExpr(hasParent(exprWithCleanups( hasParent(returnStmt())))))))) .bind("e"), expr(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 && "Unknown 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 expression, 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