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

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

The 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 androidx.room.compiler.processing.XElementKt.isField;
import static androidx.room.compiler.processing.XElementKt.isTypeElement;
import static dagger.internal.codegen.base.FrameworkTypes.isFrameworkType;
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 dagger.internal.codegen.xprocessing.XElements.asField;
import static dagger.internal.codegen.xprocessing.XElements.asTypeElement;
import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
import static dagger.internal.codegen.xprocessing.XTypes.isRawParameterizedType;
import static dagger.internal.codegen.xprocessing.XTypes.isTypeOf;
import static dagger.internal.codegen.xprocessing.XTypes.isWildcard;

import androidx.room.compiler.processing.XAnnotation;
import androidx.room.compiler.processing.XElement;
import androidx.room.compiler.processing.XFieldElement;
import androidx.room.compiler.processing.XProcessingEnv;
import androidx.room.compiler.processing.XType;
import androidx.room.compiler.processing.XTypeElement;
import androidx.room.compiler.processing.XVariableElement;
import com.google.common.collect.ImmutableSet;
import dagger.internal.codegen.base.FrameworkTypes;
import dagger.internal.codegen.base.RequestKinds;
import dagger.internal.codegen.binding.InjectionAnnotations;
import dagger.internal.codegen.javapoet.TypeNames;
import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
import dagger.internal.codegen.model.RequestKind;
import dagger.internal.codegen.xprocessing.XTypes;
import java.util.Optional;
import javax.inject.Inject;

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

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

  /**
   * 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, XElement requestElement, XType requestType) {
    if (requestElement.hasAnnotation(TypeNames.ASSISTED)) {
      // 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.
   *
   * 

See https://youtrack.jetbrains.com/issue/KT-34684. */ private boolean missingQualifierMetadata(XElement requestElement) { if (isField(requestElement)) { XFieldElement fieldElement = asField(requestElement); // static/top-level injected fields are not supported, // so no need to get qualifier from kotlin metadata if (!fieldElement.isStatic() && isTypeElement(fieldElement.getEnclosingElement()) && metadataUtil.hasMetadata(fieldElement) && metadataUtil.isMissingSyntheticPropertyForAnnotations(fieldElement)) { Optional membersInjector = Optional.ofNullable( processingEnv.findTypeElement( membersInjectorNameForType(asTypeElement(fieldElement.getEnclosingElement())))); return !membersInjector.isPresent(); } } return false; } private final class Validator { private final ValidationReport.Builder report; private final XElement requestElement; private final XType requestType; private final ImmutableSet qualifiers; Validator(ValidationReport.Builder report, XElement requestElement, XType requestType) { this.report = report; this.requestElement = requestElement; this.requestType = requestType; this.qualifiers = injectionAnnotations.getQualifiers(requestElement); } void validate() { checkQualifiers(); checkType(); } private void checkQualifiers() { if (qualifiers.size() > 1) { for (XAnnotation qualifier : qualifiers) { report.addError( "A single dependency request may not use more than one @Qualifier", requestElement, qualifier); } } } private void checkType() { if (isFrameworkType(requestType) && isRawParameterizedType(requestType)) { report.addError( "Dagger does not support injecting raw type: " + XTypes.toStableString(requestType), requestElement); // If the requested type is a raw framework type then skip the remaining checks as they // will just be noise. return; } XType keyType = extractKeyType(requestType); if (qualifiers.isEmpty() && isDeclared(keyType)) { XTypeElement typeElement = keyType.getTypeElement(); if (isAssistedInjectionType(typeElement)) { report.addError( "Dagger does not support injecting @AssistedInject type, " + XTypes.toStableString(requestType) + ". Did you mean to inject its assisted factory type instead?", requestElement); } RequestKind requestKind = RequestKinds.getRequestKind(requestType); 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 " + XTypes.toStableString(keyType), requestElement); } } if (isWildcard(keyType)) { // 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 " + XTypes.toStableString(keyType), requestElement); } if (isTypeOf(keyType, TypeNames.MEMBERS_INJECTOR)) { if (keyType.getTypeArguments().isEmpty()) { report.addError("Cannot inject a raw MembersInjector", requestElement); } else { report.addSubreport( membersInjectionValidator.validateMembersInjectionRequest( requestElement, keyType.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, XVariableElement requestElement) { XType requestType = requestElement.getType(); if (FrameworkTypes.isProducerType(requestType)) { report.addError( String.format( "%s may only be injected in @Produces methods", getSimpleName(requestType.getTypeElement())), requestElement); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy