forked from OSchip/llvm-project
156 lines
6.4 KiB
C++
156 lines
6.4 KiB
C++
//===--- UpgradeDurationConversionsCheck.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 "UpgradeDurationConversionsCheck.h"
|
|
#include "DurationRewriter.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
|
|
|
using namespace clang::ast_matchers;
|
|
|
|
namespace clang {
|
|
namespace tidy {
|
|
namespace abseil {
|
|
|
|
void UpgradeDurationConversionsCheck::registerMatchers(MatchFinder *Finder) {
|
|
if (!getLangOpts().CPlusPlus)
|
|
return;
|
|
|
|
// For the arithmetic calls, we match only the uses of the templated operators
|
|
// where the template parameter is not a built-in type. This means the
|
|
// instantiation makes use of an available user defined conversion to
|
|
// `int64_t`.
|
|
//
|
|
// The implementation of these templates will be updated to fail SFINAE for
|
|
// non-integral types. We match them to suggest an explicit cast.
|
|
|
|
// Match expressions like `a *= b` and `a /= b` where `a` has type
|
|
// `absl::Duration` and `b` is not of a built-in type.
|
|
Finder->addMatcher(
|
|
cxxOperatorCallExpr(
|
|
argumentCountIs(2),
|
|
hasArgument(
|
|
0, expr(hasType(cxxRecordDecl(hasName("::absl::Duration"))))),
|
|
hasArgument(1, expr().bind("arg")),
|
|
callee(functionDecl(
|
|
hasParent(functionTemplateDecl()),
|
|
unless(hasTemplateArgument(0, refersToType(builtinType()))),
|
|
hasAnyName("operator*=", "operator/=")))),
|
|
this);
|
|
|
|
// Match expressions like `a.operator*=(b)` and `a.operator/=(b)` where `a`
|
|
// has type `absl::Duration` and `b` is not of a built-in type.
|
|
Finder->addMatcher(
|
|
cxxMemberCallExpr(
|
|
callee(cxxMethodDecl(
|
|
ofClass(cxxRecordDecl(hasName("::absl::Duration"))),
|
|
hasParent(functionTemplateDecl()),
|
|
unless(hasTemplateArgument(0, refersToType(builtinType()))),
|
|
hasAnyName("operator*=", "operator/="))),
|
|
argumentCountIs(1), hasArgument(0, expr().bind("arg"))),
|
|
this);
|
|
|
|
// Match expressions like `a * b`, `a / b`, `operator*(a, b)`, and
|
|
// `operator/(a, b)` where `a` has type `absl::Duration` and `b` is not of a
|
|
// built-in type.
|
|
Finder->addMatcher(
|
|
callExpr(callee(functionDecl(
|
|
hasParent(functionTemplateDecl()),
|
|
unless(hasTemplateArgument(0, refersToType(builtinType()))),
|
|
hasAnyName("::absl::operator*", "::absl::operator/"))),
|
|
argumentCountIs(2),
|
|
hasArgument(0, expr(hasType(
|
|
cxxRecordDecl(hasName("::absl::Duration"))))),
|
|
hasArgument(1, expr().bind("arg"))),
|
|
this);
|
|
|
|
// Match expressions like `a * b` and `operator*(a, b)` where `a` is not of a
|
|
// built-in type and `b` has type `absl::Duration`.
|
|
Finder->addMatcher(
|
|
callExpr(callee(functionDecl(
|
|
hasParent(functionTemplateDecl()),
|
|
unless(hasTemplateArgument(0, refersToType(builtinType()))),
|
|
hasName("::absl::operator*"))),
|
|
argumentCountIs(2), hasArgument(0, expr().bind("arg")),
|
|
hasArgument(1, expr(hasType(cxxRecordDecl(
|
|
hasName("::absl::Duration")))))),
|
|
this);
|
|
|
|
// For the factory functions, we match only the non-templated overloads that
|
|
// take an `int64_t` parameter. Within these calls, we care about implicit
|
|
// casts through a user defined conversion to `int64_t`.
|
|
//
|
|
// The factory functions will be updated to be templated and SFINAE on whether
|
|
// the template parameter is an integral type. This complements the already
|
|
// existing templated overloads that only accept floating point types.
|
|
|
|
// Match calls like:
|
|
// `absl::Nanoseconds(x)`
|
|
// `absl::Microseconds(x)`
|
|
// `absl::Milliseconds(x)`
|
|
// `absl::Seconds(x)`
|
|
// `absl::Minutes(x)`
|
|
// `absl::Hours(x)`
|
|
// where `x` is not of a built-in type.
|
|
Finder->addMatcher(
|
|
implicitCastExpr(
|
|
anyOf(hasCastKind(CK_UserDefinedConversion),
|
|
has(implicitCastExpr(hasCastKind(CK_UserDefinedConversion)))),
|
|
hasParent(callExpr(
|
|
callee(functionDecl(DurationFactoryFunction(),
|
|
unless(hasParent(functionTemplateDecl())))),
|
|
hasArgument(0, expr().bind("arg"))))),
|
|
this);
|
|
}
|
|
|
|
void UpgradeDurationConversionsCheck::check(
|
|
const MatchFinder::MatchResult &Result) {
|
|
const llvm::StringRef Message =
|
|
"implicit conversion to 'int64_t' is deprecated in this context; use an "
|
|
"explicit cast instead";
|
|
|
|
const auto *ArgExpr = Result.Nodes.getNodeAs<Expr>("arg");
|
|
SourceLocation Loc = ArgExpr->getBeginLoc();
|
|
|
|
if (!match(isInTemplateInstantiation(), *ArgExpr, *Result.Context).empty()) {
|
|
if (MatchedTemplateLocations.count(Loc.getRawEncoding()) == 0) {
|
|
// For each location matched in a template instantiation, we check if the
|
|
// location can also be found in `MatchedTemplateLocations`. If it is not
|
|
// found, that means the expression did not create a match without the
|
|
// instantiation and depends on template parameters. A manual fix is
|
|
// probably required so we provide only a warning.
|
|
diag(Loc, Message);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// We gather source locations from template matches not in template
|
|
// instantiations for future matches.
|
|
internal::Matcher<Stmt> IsInsideTemplate =
|
|
hasAncestor(decl(anyOf(classTemplateDecl(), functionTemplateDecl())));
|
|
if (!match(IsInsideTemplate, *ArgExpr, *Result.Context).empty())
|
|
MatchedTemplateLocations.insert(Loc.getRawEncoding());
|
|
|
|
DiagnosticBuilder Diag = diag(Loc, Message);
|
|
CharSourceRange SourceRange = Lexer::makeFileCharRange(
|
|
CharSourceRange::getTokenRange(ArgExpr->getSourceRange()),
|
|
*Result.SourceManager, Result.Context->getLangOpts());
|
|
if (SourceRange.isInvalid())
|
|
// An invalid source range likely means we are inside a macro body. A manual
|
|
// fix is likely needed so we do not create a fix-it hint.
|
|
return;
|
|
|
|
Diag << FixItHint::CreateInsertion(SourceRange.getBegin(),
|
|
"static_cast<int64_t>(")
|
|
<< FixItHint::CreateInsertion(SourceRange.getEnd(), ")");
|
|
}
|
|
|
|
} // namespace abseil
|
|
} // namespace tidy
|
|
} // namespace clang
|