15d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin/*
25d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin * Copyright (C) 2014 Google, Inc.
35d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin *
45d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin * Licensed under the Apache License, Version 2.0 (the "License");
55d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin * you may not use this file except in compliance with the License.
65d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin * You may obtain a copy of the License at
75d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin *
85d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin * http://www.apache.org/licenses/LICENSE-2.0
95d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin *
105d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin * Unless required by applicable law or agreed to in writing, software
115d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin * distributed under the License is distributed on an "AS IS" BASIS,
125d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin * See the License for the specific language governing permissions and
145d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin * limitations under the License.
155d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin */
165d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinpackage dagger.internal.codegen;
175d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
185d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport com.google.common.collect.ImmutableSet;
195d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport com.google.common.collect.Iterables;
205d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport dagger.Module;
215d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport dagger.Provides;
225d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport java.util.Set;
235d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport javax.lang.model.element.AnnotationMirror;
245d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport javax.lang.model.element.Element;
255d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport javax.lang.model.element.ExecutableElement;
265d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport javax.lang.model.element.Modifier;
275d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport javax.lang.model.element.TypeElement;
285d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport javax.lang.model.type.DeclaredType;
295d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport javax.lang.model.type.TypeKind;
305d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport javax.lang.model.type.TypeMirror;
315d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport javax.lang.model.util.Elements;
325d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
335d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static com.google.auto.common.MoreElements.isAnnotationPresent;
345d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static com.google.common.base.Preconditions.checkArgument;
355d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static com.google.common.base.Preconditions.checkNotNull;
365d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_ABSTRACT;
375d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_MUST_RETURN_A_VALUE;
385d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_NOT_IN_MODULE;
395d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_NOT_MAP_HAS_MAP_KEY;
405d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_PRIVATE;
415d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_SET_VALUES_RAW_SET;
425d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_TYPE_PARAMETER;
435d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_WITH_MULTIPLE_MAP_KEY;
445d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static dagger.internal.codegen.ErrorMessages.BINDING_METHOD_WITH_NO_MAP_KEY;
455d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static dagger.internal.codegen.ErrorMessages.PROVIDES_METHOD_RETURN_TYPE;
465d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static dagger.internal.codegen.ErrorMessages.PROVIDES_METHOD_SET_VALUES_RETURN_SET;
475d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static dagger.internal.codegen.ErrorMessages.PROVIDES_OR_PRODUCES_METHOD_MULTIPLE_QUALIFIERS;
485d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static dagger.internal.codegen.InjectionAnnotations.getQualifiers;
495d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static dagger.internal.codegen.MapKeys.getMapKeys;
505d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static javax.lang.model.element.Modifier.ABSTRACT;
515d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static javax.lang.model.element.Modifier.PRIVATE;
525d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static javax.lang.model.type.TypeKind.ARRAY;
535d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static javax.lang.model.type.TypeKind.DECLARED;
545d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinimport static javax.lang.model.type.TypeKind.VOID;
555d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
565d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin/**
575d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin * A {@linkplain ValidationReport validator} for {@link Provides} methods.
585d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin *
595d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin * @author Gregory Kick
605d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin * @since 2.0
615d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin */
625d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffinfinal class ProvidesMethodValidator {
635d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  private final Elements elements;
645d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
655d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  ProvidesMethodValidator(Elements elements) {
665d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    this.elements = checkNotNull(elements);
675d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  }
685d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
695d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  private TypeElement getSetElement() {
705d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    return elements.getTypeElement(Set.class.getCanonicalName());
715d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  }
725d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
735d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  ValidationReport<ExecutableElement> validate(ExecutableElement providesMethodElement) {
745d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    ValidationReport.Builder<ExecutableElement> builder =
755d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        ValidationReport.about(providesMethodElement);
765d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
775d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    Provides providesAnnotation = providesMethodElement.getAnnotation(Provides.class);
785d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    checkArgument(providesAnnotation != null);
795d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
805d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    Element enclosingElement = providesMethodElement.getEnclosingElement();
815d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    if (!isAnnotationPresent(enclosingElement, Module.class)) {
825d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      builder.addError(
835d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin          formatModuleErrorMessage(BINDING_METHOD_NOT_IN_MODULE), providesMethodElement);
845d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    }
855d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
865d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    if (!providesMethodElement.getTypeParameters().isEmpty()) {
875d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      builder.addError(formatErrorMessage(BINDING_METHOD_TYPE_PARAMETER), providesMethodElement);
885d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    }
895d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
905d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    Set<Modifier> modifiers = providesMethodElement.getModifiers();
915d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    if (modifiers.contains(PRIVATE)) {
925d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      builder.addError(formatErrorMessage(BINDING_METHOD_PRIVATE), providesMethodElement);
935d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    }
945d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    if (modifiers.contains(ABSTRACT)) {
955d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      builder.addError(formatErrorMessage(BINDING_METHOD_ABSTRACT), providesMethodElement);
965d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    }
975d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
985d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    TypeMirror returnType = providesMethodElement.getReturnType();
995d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    TypeKind returnTypeKind = returnType.getKind();
1005d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    if (returnTypeKind.equals(VOID)) {
1015d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      builder.addError(
1025d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin          formatErrorMessage(BINDING_METHOD_MUST_RETURN_A_VALUE), providesMethodElement);
1035d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    }
1045d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
1055d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    // check mapkey is right
1065d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    if (!providesAnnotation.type().equals(Provides.Type.MAP)
1075d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        && !getMapKeys(providesMethodElement).isEmpty()) {
1085d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      builder.addError(
1095d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin          formatErrorMessage(BINDING_METHOD_NOT_MAP_HAS_MAP_KEY), providesMethodElement);
1105d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    }
1115d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
1125d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    validateMethodQualifiers(builder, providesMethodElement);
1135d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
1145d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    switch (providesAnnotation.type()) {
1155d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      case UNIQUE: // fall through
1165d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      case SET:
1175d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        validateKeyType(builder, returnType);
1185d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        break;
1195d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      case MAP:
1205d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        validateKeyType(builder, returnType);
1215d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        ImmutableSet<? extends AnnotationMirror> mapKeys = getMapKeys(providesMethodElement);
1225d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        switch (mapKeys.size()) {
1235d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin          case 0:
1245d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin            builder.addError(
1255d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin                formatErrorMessage(BINDING_METHOD_WITH_NO_MAP_KEY), providesMethodElement);
1265d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin            break;
1275d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin          case 1:
1285d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin            break;
1295d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin          default:
1305d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin            builder.addError(
1315d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin                formatErrorMessage(BINDING_METHOD_WITH_MULTIPLE_MAP_KEY), providesMethodElement);
1325d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin            break;
1335d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        }
1345d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        break;
1355d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      case SET_VALUES:
1365d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        if (!returnTypeKind.equals(DECLARED)) {
1375d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin          builder.addError(PROVIDES_METHOD_SET_VALUES_RETURN_SET, providesMethodElement);
1385d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        } else {
1395d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin          DeclaredType declaredReturnType = (DeclaredType) returnType;
1405d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin          // TODO(gak): should we allow "covariant return" for set values?
1415d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin          if (!declaredReturnType.asElement().equals(getSetElement())) {
1425d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin            builder.addError(PROVIDES_METHOD_SET_VALUES_RETURN_SET, providesMethodElement);
1435d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin          } else if (declaredReturnType.getTypeArguments().isEmpty()) {
1445d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin            builder.addError(
1455d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin                formatErrorMessage(BINDING_METHOD_SET_VALUES_RAW_SET), providesMethodElement);
1465d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin          } else {
1475d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin            validateKeyType(builder,
1485d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin                Iterables.getOnlyElement(declaredReturnType.getTypeArguments()));
1495d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin          }
1505d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        }
1515d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        break;
1525d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      default:
1535d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        throw new AssertionError();
1545d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    }
1555d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
1565d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    return builder.build();
1575d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  }
1585d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
1595d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  /** Validates that a Provides or Produces method doesn't have multiple qualifiers. */
1605d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  static void validateMethodQualifiers(ValidationReport.Builder<ExecutableElement> builder,
1615d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      ExecutableElement methodElement) {
1625d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    ImmutableSet<? extends AnnotationMirror> qualifiers = getQualifiers(methodElement);
1635d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    if (qualifiers.size() > 1) {
1645d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      for (AnnotationMirror qualifier : qualifiers) {
1655d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin        builder.addError(PROVIDES_OR_PRODUCES_METHOD_MULTIPLE_QUALIFIERS, methodElement, qualifier);
1665d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      }
1675d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    }
1685d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  }
1695d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
1705d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  private String formatErrorMessage(String msg) {
1715d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    return String.format(msg, Provides.class.getSimpleName());
1725d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  }
1735d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
1745d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  private String formatModuleErrorMessage(String msg) {
1755d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    return String.format(msg, Provides.class.getSimpleName(), Module.class.getSimpleName());
1765d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  }
1775d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin
1785d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  private void validateKeyType(ValidationReport.Builder<? extends Element> reportBuilder,
1795d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      TypeMirror type) {
1805d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    TypeKind kind = type.getKind();
1815d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    if (!(kind.isPrimitive() || kind.equals(DECLARED) || kind.equals(ARRAY))) {
1825d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin      reportBuilder.addError(PROVIDES_METHOD_RETURN_TYPE, reportBuilder.getSubject());
1835d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin    }
1845d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin  }
1855d3207ac2713386ed61c6ca8f0356e8f093a62e1Paul Duffin}
186