forked from OSchip/llvm-project
119 lines
4.7 KiB
C++
119 lines
4.7 KiB
C++
//===--- DurationUnnecessaryConversionCheck.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 "DurationUnnecessaryConversionCheck.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 DurationUnnecessaryConversionCheck::registerMatchers(MatchFinder *Finder) {
|
|
for (const auto &Scale : {"Hours", "Minutes", "Seconds", "Milliseconds",
|
|
"Microseconds", "Nanoseconds"}) {
|
|
std::string DurationFactory = (llvm::Twine("::absl::") + Scale).str();
|
|
std::string FloatConversion =
|
|
(llvm::Twine("::absl::ToDouble") + Scale).str();
|
|
std::string IntegerConversion =
|
|
(llvm::Twine("::absl::ToInt64") + Scale).str();
|
|
|
|
// Matcher which matches the current scale's factory with a `1` argument,
|
|
// e.g. `absl::Seconds(1)`.
|
|
auto FactoryMatcher = ignoringElidableConstructorCall(
|
|
callExpr(callee(functionDecl(hasName(DurationFactory))),
|
|
hasArgument(0, ignoringImpCasts(integerLiteral(equals(1))))));
|
|
|
|
// Matcher which matches either inverse function and binds its argument,
|
|
// e.g. `absl::ToDoubleSeconds(dur)`.
|
|
auto InverseFunctionMatcher = callExpr(
|
|
callee(functionDecl(hasAnyName(FloatConversion, IntegerConversion))),
|
|
hasArgument(0, expr().bind("arg")));
|
|
|
|
// Matcher which matches a duration divided by the factory_matcher above,
|
|
// e.g. `dur / absl::Seconds(1)`.
|
|
auto DivisionOperatorMatcher = cxxOperatorCallExpr(
|
|
hasOverloadedOperatorName("/"), hasArgument(0, expr().bind("arg")),
|
|
hasArgument(1, FactoryMatcher));
|
|
|
|
// Matcher which matches a duration argument to `FDivDuration`,
|
|
// e.g. `absl::FDivDuration(dur, absl::Seconds(1))`
|
|
auto FdivMatcher = callExpr(
|
|
callee(functionDecl(hasName("::absl::FDivDuration"))),
|
|
hasArgument(0, expr().bind("arg")), hasArgument(1, FactoryMatcher));
|
|
|
|
// Matcher which matches a duration argument being scaled,
|
|
// e.g. `absl::ToDoubleSeconds(dur) * 2`
|
|
auto ScalarMatcher = ignoringImpCasts(
|
|
binaryOperator(hasOperatorName("*"),
|
|
hasEitherOperand(expr(ignoringParenImpCasts(
|
|
callExpr(callee(functionDecl(hasAnyName(
|
|
FloatConversion, IntegerConversion))),
|
|
hasArgument(0, expr().bind("arg")))
|
|
.bind("inner_call")))))
|
|
.bind("binop"));
|
|
|
|
Finder->addMatcher(
|
|
callExpr(callee(functionDecl(hasName(DurationFactory))),
|
|
hasArgument(0, anyOf(InverseFunctionMatcher,
|
|
DivisionOperatorMatcher, FdivMatcher,
|
|
ScalarMatcher)))
|
|
.bind("call"),
|
|
this);
|
|
}
|
|
}
|
|
|
|
void DurationUnnecessaryConversionCheck::check(
|
|
const MatchFinder::MatchResult &Result) {
|
|
const auto *OuterCall = Result.Nodes.getNodeAs<Expr>("call");
|
|
|
|
if (isInMacro(Result, OuterCall))
|
|
return;
|
|
|
|
FixItHint Hint;
|
|
if (const auto *Binop = Result.Nodes.getNodeAs<BinaryOperator>("binop")) {
|
|
const auto *Arg = Result.Nodes.getNodeAs<Expr>("arg");
|
|
const auto *InnerCall = Result.Nodes.getNodeAs<Expr>("inner_call");
|
|
const Expr *LHS = Binop->getLHS();
|
|
const Expr *RHS = Binop->getRHS();
|
|
|
|
if (LHS->IgnoreParenImpCasts() == InnerCall) {
|
|
Hint = FixItHint::CreateReplacement(
|
|
OuterCall->getSourceRange(),
|
|
(llvm::Twine(tooling::fixit::getText(*Arg, *Result.Context)) + " * " +
|
|
tooling::fixit::getText(*RHS, *Result.Context))
|
|
.str());
|
|
} else {
|
|
assert(RHS->IgnoreParenImpCasts() == InnerCall &&
|
|
"Inner call should be find on the RHS");
|
|
|
|
Hint = FixItHint::CreateReplacement(
|
|
OuterCall->getSourceRange(),
|
|
(llvm::Twine(tooling::fixit::getText(*LHS, *Result.Context)) + " * " +
|
|
tooling::fixit::getText(*Arg, *Result.Context))
|
|
.str());
|
|
}
|
|
} else if (const auto *Arg = Result.Nodes.getNodeAs<Expr>("arg")) {
|
|
Hint = FixItHint::CreateReplacement(
|
|
OuterCall->getSourceRange(),
|
|
tooling::fixit::getText(*Arg, *Result.Context));
|
|
}
|
|
diag(OuterCall->getBeginLoc(),
|
|
"remove unnecessary absl::Duration conversions")
|
|
<< Hint;
|
|
}
|
|
|
|
} // namespace abseil
|
|
} // namespace tidy
|
|
} // namespace clang
|