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 CanRunScriptChecker::registerMatchers(MatchFinder *AstMatcher) {
  auto Refcounted = qualType(hasDeclaration(cxxRecordDecl(isRefCounted())));
  auto InvalidArg =
      // We want to find any expression,
      ignoreTrivials(expr(
          // which has a refcounted pointer type,
          anyOf(
            hasType(Refcounted),
            hasType(pointsTo(Refcounted)),
            hasType(references(Refcounted))
          ),
          // and which is not this,
          unless(cxxThisExpr()),
          // and which is not a method call on a smart ptr,
          unless(cxxMemberCallExpr(on(hasType(isSmartPtrToRefCounted())))),
          // and which is not calling operator* on a smart ptr.
          unless(
            allOf(
              cxxOperatorCallExpr(hasOverloadedOperatorName("*")),
              callExpr(allOf(
                hasAnyArgument(hasType(isSmartPtrToRefCounted())),
                argumentCountIs(1)
              ))
            )
          ),
          // and which is not a parameter of the parent function,
          unless(declRefExpr(to(parmVarDecl()))),
          // and which is not a MOZ_KnownLive wrapped value.
          unless(callExpr(callee(functionDecl(hasName("MOZ_KnownLive"))))),
          expr().bind("invalidArg")));

  auto OptionalInvalidExplicitArg = anyOf(
      // We want to find any argument which is invalid.
      hasAnyArgument(InvalidArg),

      // This makes this matcher optional.
      anything());

  // Please note that the hasCanRunScriptAnnotation() matchers are not present
  // directly in the cxxMemberCallExpr, callExpr and constructExpr matchers
  // because we check that the corresponding functions can run script later in
  // the checker code.
  AstMatcher->addMatcher(
      expr(
          anyOf(
              // We want to match a method call expression,
              cxxMemberCallExpr(
                  // which optionally has an invalid arg,
                  OptionalInvalidExplicitArg,
                  // or which optionally has an invalid implicit this argument,
                  anyOf(
                      // which derefs into an invalid arg,
                      on(cxxOperatorCallExpr(
                          anyOf(hasAnyArgument(InvalidArg), anything()))),
                      // or is an invalid arg.
                      on(InvalidArg),

                      anything()),
                  expr().bind("callExpr")),
              // or a regular call expression,
              callExpr(
                  // which optionally has an invalid arg.
                  OptionalInvalidExplicitArg, expr().bind("callExpr")),
              // or a construct expression,
              cxxConstructExpr(
                  // which optionally has an invalid arg.
                  OptionalInvalidExplicitArg, expr().bind("constructExpr"))),

          anyOf(
              // We want to match the parent function.
              forFunction(functionDecl().bind("nonCanRunScriptParentFunction")),

              // ... optionally.
              anything())),
      this);
}