123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- //===--- 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"
- #include "clang/Lex/Lexer.h"
- using namespace clang::ast_matchers;
- namespace clang::tidy::abseil {
- void UpgradeDurationConversionsCheck::registerMatchers(MatchFinder *Finder) {
- // 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/="))))
- .bind("OuterExpr"),
- 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")))
- .bind("OuterExpr"),
- 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")))
- .bind("OuterExpr"),
- 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"))))))
- .bind("OuterExpr"),
- 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(
- traverse(TK_AsIs, implicitCastExpr(
- anyOf(hasCastKind(CK_UserDefinedConversion),
- has(implicitCastExpr(
- hasCastKind(CK_UserDefinedConversion)))),
- hasParent(callExpr(
- callee(functionDecl(
- DurationFactoryFunction(),
- unless(hasParent(functionTemplateDecl())))),
- hasArgument(0, expr().bind("arg")))))
- .bind("OuterExpr")),
- 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";
- TraversalKindScope RAII(*Result.Context, TK_AsIs);
- const auto *ArgExpr = Result.Nodes.getNodeAs<Expr>("arg");
- SourceLocation Loc = ArgExpr->getBeginLoc();
- const auto *OuterExpr = Result.Nodes.getNodeAs<Expr>("OuterExpr");
- if (!match(isInTemplateInstantiation(), *OuterExpr, *Result.Context)
- .empty()) {
- if (MatchedTemplateLocations.count(Loc) == 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);
- 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 clang::tidy::abseil
|