All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dagger.internal.codegen.validation.DependencyRequestValidator Maven / Gradle / Ivy

There is a newer version: 2.54
Show newest version
/*
 * Copyright (C) 2018 The Dagger Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package dagger.internal.codegen.validation;

import static com.google.auto.common.MoreElements.asType;
import static com.google.auto.common.MoreElements.asVariable;
import static com.google.auto.common.MoreTypes.asTypeElement;
import static dagger.internal.codegen.base.RequestKinds.extractKeyType;
import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.isAssistedFactoryType;
import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.isAssistedInjectionType;
import static dagger.internal.codegen.binding.SourceFiles.membersInjectorNameForType;
import static javax.lang.model.element.Modifier.STATIC;
import static javax.lang.model.type.TypeKind.WILDCARD;

import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
import com.google.common.collect.ImmutableCollection;
import dagger.MembersInjector;
import dagger.assisted.Assisted;
import dagger.internal.codegen.base.FrameworkTypes;
import dagger.internal.codegen.base.RequestKinds;
import dagger.internal.codegen.binding.InjectionAnnotations;
import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
import dagger.internal.codegen.langmodel.DaggerElements;
import dagger.spi.model.RequestKind;
import java.util.Optional;
import javax.inject.Inject;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;

/** Validation for dependency requests. */
final class DependencyRequestValidator {
  private final MembersInjectionValidator membersInjectionValidator;
  private final InjectionAnnotations injectionAnnotations;
  private final KotlinMetadataUtil metadataUtil;
  private final DaggerElements elements;

  @Inject
  DependencyRequestValidator(
      MembersInjectionValidator membersInjectionValidator,
      InjectionAnnotations injectionAnnotations,
      KotlinMetadataUtil metadataUtil,
      DaggerElements elements) {
    this.membersInjectionValidator = membersInjectionValidator;
    this.injectionAnnotations = injectionAnnotations;
    this.metadataUtil = metadataUtil;
    this.elements = elements;
  }

  /**
   * Adds an error if the given dependency request has more than one qualifier annotation or is a
   * non-instance request with a wildcard type.
   */
  void validateDependencyRequest(
      ValidationReport.Builder report, Element requestElement, TypeMirror requestType) {
    if (MoreElements.isAnnotationPresent(requestElement, Assisted.class)) {
      // Don't validate assisted parameters. These are not dependency requests.
      return;
    }
    if (missingQualifierMetadata(requestElement)) {
      report.addError(
          "Unable to read annotations on an injected Kotlin property. The Dagger compiler must"
              + " also be applied to any project containing @Inject properties.",
          requestElement);

      // Skip any further validation if we don't have valid metadata for a type that needs it.
      return;
    }

    new Validator(report, requestElement, requestType).validate();
  }

  /** Returns {@code true} if a kotlin inject field is missing metadata about its qualifiers. */
  private boolean missingQualifierMetadata(Element requestElement) {
    if (requestElement.getKind() == ElementKind.FIELD
        // static injected fields are not supported, no need to get qualifier from kotlin metadata
        && !requestElement.getModifiers().contains(STATIC)
        && metadataUtil.hasMetadata(requestElement)
        && metadataUtil.isMissingSyntheticPropertyForAnnotations(asVariable(requestElement))) {
      Optional membersInjector =
          Optional.ofNullable(
              elements.getTypeElement(
                  membersInjectorNameForType(asType(requestElement.getEnclosingElement()))));
      return !membersInjector.isPresent();
    }
    return false;
  }

  private final class Validator {
    private final ValidationReport.Builder report;
    private final Element requestElement;
    private final TypeMirror requestType;
    private final TypeMirror keyType;
    private final RequestKind requestKind;
    private final ImmutableCollection qualifiers;


    Validator(ValidationReport.Builder report, Element requestElement, TypeMirror requestType) {
      this.report = report;
      this.requestElement = requestElement;
      this.requestType = requestType;
      this.keyType = extractKeyType(requestType);
      this.requestKind = RequestKinds.getRequestKind(requestType);
      this.qualifiers = injectionAnnotations.getQualifiers(requestElement);
    }

    void validate() {
      checkQualifiers();
      checkType();
    }

    private void checkQualifiers() {
      if (qualifiers.size() > 1) {
        for (AnnotationMirror qualifier : qualifiers) {
          report.addError(
              "A single dependency request may not use more than one @Qualifier",
              requestElement,
              qualifier);
        }
      }
    }

    private void checkType() {
      if (qualifiers.isEmpty() && keyType.getKind() == TypeKind.DECLARED) {
        TypeElement typeElement = asTypeElement(keyType);
        if (isAssistedInjectionType(typeElement)) {
          report.addError(
              "Dagger does not support injecting @AssistedInject type, "
                  + requestType
                  + ". Did you mean to inject its assisted factory type instead?",
              requestElement);
        }
        if (!(requestKind == RequestKind.INSTANCE || requestKind == RequestKind.PROVIDER)
            && isAssistedFactoryType(typeElement)) {
          report.addError(
              "Dagger does not support injecting Lazy, Producer, "
                  + "or Produced when T is an @AssistedFactory-annotated type such as "
                  + keyType,
              requestElement);
        }
      }
      if (keyType.getKind().equals(WILDCARD)) {
        // TODO(ronshapiro): Explore creating this message using RequestKinds.
        report.addError(
            "Dagger does not support injecting Provider, Lazy, Producer, "
                + "or Produced when T is a wildcard type such as "
                + keyType,
            requestElement);
      }
      if (MoreTypes.isType(keyType) && MoreTypes.isTypeOf(MembersInjector.class, keyType)) {
        DeclaredType membersInjectorType = MoreTypes.asDeclared(keyType);
        if (membersInjectorType.getTypeArguments().isEmpty()) {
          report.addError("Cannot inject a raw MembersInjector", requestElement);
        } else {
          report.addSubreport(
              membersInjectionValidator.validateMembersInjectionRequest(
                  requestElement, membersInjectorType.getTypeArguments().get(0)));
        }
      }
    }
  }

  /**
   * Adds an error if the given dependency request is for a {@link dagger.producers.Producer} or
   * {@link dagger.producers.Produced}.
   *
   * 

Only call this when processing a provision binding. */ // TODO(dpb): Should we disallow Producer entry points in non-production components? void checkNotProducer(ValidationReport.Builder report, VariableElement requestElement) { TypeMirror requestType = requestElement.asType(); if (FrameworkTypes.isProducerType(requestType)) { report.addError( String.format( "%s may only be injected in @Produces methods", MoreTypes.asTypeElement(requestType).getSimpleName()), requestElement); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy