brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.8 KiB · 1a6ff30 Raw
162 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "UpgradeDurationConversionsCheck.h"10#include "DurationRewriter.h"11#include "clang/AST/ASTContext.h"12#include "clang/ASTMatchers/ASTMatchFinder.h"13#include "clang/Lex/Lexer.h"14 15using namespace clang::ast_matchers;16 17namespace clang::tidy::abseil {18 19void UpgradeDurationConversionsCheck::registerMatchers(MatchFinder *Finder) {20  // For the arithmetic calls, we match only the uses of the templated operators21  // where the template parameter is not a built-in type. This means the22  // instantiation makes use of an available user defined conversion to23  // `int64_t`.24  //25  // The implementation of these templates will be updated to fail SFINAE for26  // non-integral types. We match them to suggest an explicit cast.27 28  // Match expressions like `a *= b` and `a /= b` where `a` has type29  // `absl::Duration` and `b` is not of a built-in type.30  Finder->addMatcher(31      cxxOperatorCallExpr(32          argumentCountIs(2),33          hasArgument(34              0, expr(hasType(cxxRecordDecl(hasName("::absl::Duration"))))),35          hasArgument(1, expr().bind("arg")),36          callee(functionDecl(37              hasParent(functionTemplateDecl()),38              unless(hasTemplateArgument(0, refersToType(builtinType()))),39              hasAnyName("operator*=", "operator/="))))40          .bind("OuterExpr"),41      this);42 43  // Match expressions like `a.operator*=(b)` and `a.operator/=(b)` where `a`44  // has type `absl::Duration` and `b` is not of a built-in type.45  Finder->addMatcher(46      cxxMemberCallExpr(47          callee(cxxMethodDecl(48              ofClass(cxxRecordDecl(hasName("::absl::Duration"))),49              hasParent(functionTemplateDecl()),50              unless(hasTemplateArgument(0, refersToType(builtinType()))),51              hasAnyName("operator*=", "operator/="))),52          argumentCountIs(1), hasArgument(0, expr().bind("arg")))53          .bind("OuterExpr"),54      this);55 56  // Match expressions like `a * b`, `a / b`, `operator*(a, b)`, and57  // `operator/(a, b)` where `a` has type `absl::Duration` and `b` is not of a58  // built-in type.59  Finder->addMatcher(60      callExpr(callee(functionDecl(61                   hasParent(functionTemplateDecl()),62                   unless(hasTemplateArgument(0, refersToType(builtinType()))),63                   hasAnyName("::absl::operator*", "::absl::operator/"))),64               argumentCountIs(2),65               hasArgument(0, expr(hasType(66                                  cxxRecordDecl(hasName("::absl::Duration"))))),67               hasArgument(1, expr().bind("arg")))68          .bind("OuterExpr"),69      this);70 71  // Match expressions like `a * b` and `operator*(a, b)` where `a` is not of a72  // built-in type and `b` has type `absl::Duration`.73  Finder->addMatcher(74      callExpr(callee(functionDecl(75                   hasParent(functionTemplateDecl()),76                   unless(hasTemplateArgument(0, refersToType(builtinType()))),77                   hasName("::absl::operator*"))),78               argumentCountIs(2), hasArgument(0, expr().bind("arg")),79               hasArgument(1, expr(hasType(80                                  cxxRecordDecl(hasName("::absl::Duration"))))))81          .bind("OuterExpr"),82      this);83 84  // For the factory functions, we match only the non-templated overloads that85  // take an `int64_t` parameter. Within these calls, we care about implicit86  // casts through a user defined conversion to `int64_t`.87  //88  // The factory functions will be updated to be templated and SFINAE on whether89  // the template parameter is an integral type. This complements the already90  // existing templated overloads that only accept floating point types.91 92  // Match calls like:93  //   `absl::Nanoseconds(x)`94  //   `absl::Microseconds(x)`95  //   `absl::Milliseconds(x)`96  //   `absl::Seconds(x)`97  //   `absl::Minutes(x)`98  //   `absl::Hours(x)`99  // where `x` is not of a built-in type.100  Finder->addMatcher(101      traverse(TK_AsIs, implicitCastExpr(102                            anyOf(hasCastKind(CK_UserDefinedConversion),103                                  has(implicitCastExpr(104                                      hasCastKind(CK_UserDefinedConversion)))),105                            hasParent(callExpr(106                                callee(functionDecl(107                                    durationFactoryFunction(),108                                    unless(hasParent(functionTemplateDecl())))),109                                hasArgument(0, expr().bind("arg")))))110                            .bind("OuterExpr")),111      this);112}113 114void UpgradeDurationConversionsCheck::check(115    const MatchFinder::MatchResult &Result) {116  const llvm::StringRef Message =117      "implicit conversion to 'int64_t' is deprecated in this context; use an "118      "explicit cast instead";119 120  const TraversalKindScope RAII(*Result.Context, TK_AsIs);121 122  const auto *ArgExpr = Result.Nodes.getNodeAs<Expr>("arg");123  const SourceLocation Loc = ArgExpr->getBeginLoc();124 125  const auto *OuterExpr = Result.Nodes.getNodeAs<Expr>("OuterExpr");126 127  if (!match(isInTemplateInstantiation(), *OuterExpr, *Result.Context)128           .empty()) {129    if (!MatchedTemplateLocations.contains(Loc)) {130      // For each location matched in a template instantiation, we check if the131      // location can also be found in `MatchedTemplateLocations`. If it is not132      // found, that means the expression did not create a match without the133      // instantiation and depends on template parameters. A manual fix is134      // probably required so we provide only a warning.135      diag(Loc, Message);136    }137    return;138  }139 140  // We gather source locations from template matches not in template141  // instantiations for future matches.142  const internal::Matcher<Stmt> IsInsideTemplate =143      hasAncestor(decl(anyOf(classTemplateDecl(), functionTemplateDecl())));144  if (!match(IsInsideTemplate, *ArgExpr, *Result.Context).empty())145    MatchedTemplateLocations.insert(Loc);146 147  const DiagnosticBuilder Diag = diag(Loc, Message);148  const CharSourceRange SourceRange = Lexer::makeFileCharRange(149      CharSourceRange::getTokenRange(ArgExpr->getSourceRange()),150      *Result.SourceManager, Result.Context->getLangOpts());151  if (SourceRange.isInvalid())152    // An invalid source range likely means we are inside a macro body. A manual153    // fix is likely needed so we do not create a fix-it hint.154    return;155 156  Diag << FixItHint::CreateInsertion(SourceRange.getBegin(),157                                     "static_cast<int64_t>(")158       << FixItHint::CreateInsertion(SourceRange.getEnd(), ")");159}160 161} // namespace clang::tidy::abseil162