diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/InjectBindingValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/InjectBindingValidator.java index 21512958dca..6455f2985fd 100644 --- a/java/dagger/internal/codegen/bindinggraphvalidation/InjectBindingValidator.java +++ b/java/dagger/internal/codegen/bindinggraphvalidation/InjectBindingValidator.java @@ -33,7 +33,7 @@ final class InjectBindingValidator extends ValidationBindingGraphPlugin { @Inject InjectBindingValidator(InjectValidator injectValidator) { - this.injectValidator = injectValidator.whenGeneratingCode(); + this.injectValidator = injectValidator; } @Override @@ -50,7 +50,8 @@ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticR private void validateInjectionBinding(Binding node, DiagnosticReporter diagnosticReporter) { ValidationReport typeReport = - injectValidator.validate(node.key().type().xprocessing().getTypeElement()); + injectValidator.validateWhenGeneratingCode( + node.key().type().xprocessing().getTypeElement()); for (Item item : typeReport.allItems()) { diagnosticReporter.reportBinding(item.kind(), node, item.message()); } diff --git a/java/dagger/internal/codegen/compileroption/CompilerOptions.java b/java/dagger/internal/codegen/compileroption/CompilerOptions.java index b34e8e1d6b0..62db93ba7f8 100644 --- a/java/dagger/internal/codegen/compileroption/CompilerOptions.java +++ b/java/dagger/internal/codegen/compileroption/CompilerOptions.java @@ -54,15 +54,6 @@ public final boolean doCheckForNulls() { */ public abstract boolean includeStacktraceWithDeferredErrorMessages(); - /** - * If {@code true}, Dagger will generate factories and components even if some members-injected - * types have {@code private} or {@code static} {@code @Inject}-annotated members. - * - *

This should only ever be enabled by the TCK tests. Disabling this validation could lead to - * generating code that does not compile. - */ - public abstract boolean ignorePrivateAndStaticInjectionForComponent(); - public abstract ValidationType scopeCycleValidationType(); /** diff --git a/java/dagger/internal/codegen/compileroption/ProcessingEnvironmentCompilerOptions.java b/java/dagger/internal/codegen/compileroption/ProcessingEnvironmentCompilerOptions.java index bd112126a9c..7e9eb41595e 100644 --- a/java/dagger/internal/codegen/compileroption/ProcessingEnvironmentCompilerOptions.java +++ b/java/dagger/internal/codegen/compileroption/ProcessingEnvironmentCompilerOptions.java @@ -29,7 +29,6 @@ import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.FLOATING_BINDS_METHODS; import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.FORMAT_GENERATED_SOURCE; import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.GENERATED_CLASS_EXTENDS_COMPONENT; -import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.IGNORE_PRIVATE_AND_STATIC_INJECTION_FOR_COMPONENT; import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.IGNORE_PROVISION_KEY_WILDCARDS; import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.INCLUDE_STACKTRACE_WITH_DEFERRED_ERROR_MESSAGES; import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.PLUGINS_VISIT_FULL_BINDING_GRAPHS; @@ -139,11 +138,6 @@ public boolean includeStacktraceWithDeferredErrorMessages() { return isEnabled(INCLUDE_STACKTRACE_WITH_DEFERRED_ERROR_MESSAGES); } - @Override - public boolean ignorePrivateAndStaticInjectionForComponent() { - return isEnabled(IGNORE_PRIVATE_AND_STATIC_INJECTION_FOR_COMPONENT); - } - @Override public ValidationType scopeCycleValidationType() { return parseOption(DISABLE_INTER_COMPONENT_SCOPE_VALIDATION); @@ -325,8 +319,6 @@ enum Feature implements EnumOption { INCLUDE_STACKTRACE_WITH_DEFERRED_ERROR_MESSAGES, - IGNORE_PRIVATE_AND_STATIC_INJECTION_FOR_COMPONENT, - EXPERIMENTAL_AHEAD_OF_TIME_SUBCOMPONENTS, FORCE_USE_SERIALIZED_COMPONENT_IMPLEMENTATIONS, diff --git a/java/dagger/internal/codegen/javac/JavacPluginCompilerOptions.java b/java/dagger/internal/codegen/javac/JavacPluginCompilerOptions.java index c2f42506680..a833c02e5f0 100644 --- a/java/dagger/internal/codegen/javac/JavacPluginCompilerOptions.java +++ b/java/dagger/internal/codegen/javac/JavacPluginCompilerOptions.java @@ -66,11 +66,6 @@ public boolean includeStacktraceWithDeferredErrorMessages() { return false; } - @Override - public boolean ignorePrivateAndStaticInjectionForComponent() { - return false; - } - @Override public ValidationType scopeCycleValidationType() { return NONE; diff --git a/java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java b/java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java index 152a4804914..ab2a5711a81 100644 --- a/java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java +++ b/java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java @@ -84,7 +84,6 @@ final class InjectBindingRegistryImpl implements InjectBindingRegistry { private final XProcessingEnv processingEnv; private final XMessager messager; private final InjectValidator injectValidator; - private final InjectValidator injectValidatorWhenGeneratingCode; private final KeyFactory keyFactory; private final BindingFactory bindingFactory; private final CompilerOptions compilerOptions; @@ -106,7 +105,7 @@ void generateBindings(SourceFileGenerator generator) throws SourceFileGenerat checkState(!binding.unresolved().isPresent()); XType type = binding.key().type().xprocessing(); if (!isDeclared(type) - || injectValidatorWhenGeneratingCode.validate(type.getTypeElement()).isClean()) { + || injectValidator.validateWhenGeneratingCode(type.getTypeElement()).isClean()) { generator.generate(binding); } materializedBindingKeys.add(binding.key()); @@ -223,7 +222,6 @@ private void tryToCacheBinding(B binding) { this.processingEnv = processingEnv; this.messager = messager; this.injectValidator = injectValidator; - this.injectValidatorWhenGeneratingCode = injectValidator.whenGeneratingCode(); this.keyFactory = keyFactory; this.bindingFactory = bindingFactory; this.compilerOptions = compilerOptions; diff --git a/java/dagger/internal/codegen/validation/InjectValidator.java b/java/dagger/internal/codegen/validation/InjectValidator.java index a3af669b4c5..135839dd92c 100644 --- a/java/dagger/internal/codegen/validation/InjectValidator.java +++ b/java/dagger/internal/codegen/validation/InjectValidator.java @@ -56,7 +56,6 @@ import javax.inject.Inject; import javax.inject.Singleton; import javax.tools.Diagnostic; -import javax.tools.Diagnostic.Kind; /** * A {@linkplain ValidationReport validator} for {@link Inject}-annotated elements and the types @@ -66,14 +65,12 @@ public final class InjectValidator implements ClearableCache { private final XProcessingEnv processingEnv; - private final CompilerOptions compilerOptions; private final DependencyRequestValidator dependencyRequestValidator; - private final Optional privateAndStaticInjectionDiagnosticKind; private final InjectionAnnotations injectionAnnotations; private final DaggerSuperficialValidation superficialValidation; - private final Map provisionReports = new HashMap<>(); - private final Map membersInjectionReports = new HashMap<>(); private final MethodSignatureFormatter methodSignatureFormatter; + private final InternalValidator validator; + private final InternalValidator validatorWhenGeneratingCode; @Inject InjectValidator( @@ -83,376 +80,390 @@ public final class InjectValidator implements ClearableCache { InjectionAnnotations injectionAnnotations, DaggerSuperficialValidation superficialValidation, MethodSignatureFormatter methodSignatureFormatter) { - this( - processingEnv, - compilerOptions, - dependencyRequestValidator, - Optional.empty(), - injectionAnnotations, - superficialValidation, - methodSignatureFormatter); - } - - private InjectValidator( - XProcessingEnv processingEnv, - CompilerOptions compilerOptions, - DependencyRequestValidator dependencyRequestValidator, - Optional privateAndStaticInjectionDiagnosticKind, - InjectionAnnotations injectionAnnotations, - DaggerSuperficialValidation superficialValidation, - MethodSignatureFormatter methodSignatureFormatter) { this.processingEnv = processingEnv; - this.compilerOptions = compilerOptions; this.dependencyRequestValidator = dependencyRequestValidator; - this.privateAndStaticInjectionDiagnosticKind = privateAndStaticInjectionDiagnosticKind; this.injectionAnnotations = injectionAnnotations; this.superficialValidation = superficialValidation; this.methodSignatureFormatter = methodSignatureFormatter; + + // When validating types that require a generated factory class we need to error on private and + // static inject members even if the compiler options are set to not error. + this.validatorWhenGeneratingCode = + new InternalValidator(Diagnostic.Kind.ERROR, Diagnostic.Kind.ERROR); + + // When validating types that might not require a generated factory we can take the user flags + // for private and static inject members into account, but try to reuse the existing one if the + // diagnostic kinds are the same. + this.validator = + (compilerOptions.privateMemberValidationKind() == Diagnostic.Kind.ERROR + && compilerOptions.staticMemberValidationKind() == Diagnostic.Kind.ERROR) + ? validatorWhenGeneratingCode + : new InternalValidator( + compilerOptions.privateMemberValidationKind(), + compilerOptions.staticMemberValidationKind()); } @Override public void clearCache() { - provisionReports.clear(); - membersInjectionReports.clear(); + validator.clearCache(); + validatorWhenGeneratingCode.clearCache(); } - /** - * Returns a new validator that performs the same validation as this one, but is strict about - * rejecting optionally-specified JSR 330 behavior that Dagger doesn't support (unless {@code - * -Adagger.ignorePrivateAndStaticInjectionForComponent=enabled} was set in the javac options). - */ - public InjectValidator whenGeneratingCode() { - return compilerOptions.ignorePrivateAndStaticInjectionForComponent() - ? this - : new InjectValidator( - processingEnv, - compilerOptions, - dependencyRequestValidator, - Optional.of(Diagnostic.Kind.ERROR), - injectionAnnotations, - superficialValidation, - methodSignatureFormatter); + public ValidationReport validate(XTypeElement typeElement) { + return validator.validate(typeElement); } - public ValidationReport validate(XTypeElement typeElement) { - return reentrantComputeIfAbsent(provisionReports, typeElement, this::validateUncached); + public ValidationReport validateForMembersInjection(XTypeElement typeElement) { + return validator.validateForMembersInjection(typeElement); } - private ValidationReport validateUncached(XTypeElement typeElement) { - ValidationReport.Builder builder = ValidationReport.about(typeElement); - builder.addSubreport(validateForMembersInjectionInternal(typeElement)); - - ImmutableSet injectConstructors = - ImmutableSet.builder() - .addAll(injectedConstructors(typeElement)) - .addAll(assistedInjectedConstructors(typeElement)) - .build(); - - switch (injectConstructors.size()) { - case 0: - break; // Nothing to validate. - case 1: - builder.addSubreport(validateConstructor(getOnlyElement(injectConstructors))); - break; - default: - builder.addError( - String.format( - "Type %s may only contain one injected constructor. Found: %s", - typeElement.getQualifiedName(), - injectConstructors.stream() - .map(methodSignatureFormatter::format) - .collect(toImmutableList())), - typeElement); + /** + * Validates {@code typeElement} that requires a factory to be generated. + * + *

In this case, the validator will have stricter validation for private and static injection + * since the generated factory doesn't support those types. + */ + public ValidationReport validateWhenGeneratingCode(XTypeElement typeElement) { + if (typeElement.getPackageName().startsWith("org.atinject.tck")) { + // The Technology Compatibility Kit (TCK) package is a special package for testing the JSR330 + // spec, which includes optional features like supporting static/private inject members. Even + // though Dagger doesn't support this, we allow it for this one case for the test coverage + // purposes. Use the normal validator which takes the user's compiler flags into account. + return validator.validate(typeElement); } - - return builder.build(); + return validatorWhenGeneratingCode.validate(typeElement); } - private ValidationReport validateConstructor(XConstructorElement constructorElement) { - superficialValidation.validateTypeOf(constructorElement); - ValidationReport.Builder builder = - ValidationReport.about(constructorElement.getEnclosingElement()); + private final class InternalValidator { + private final Diagnostic.Kind privateMemberDiagnosticKind; + private final Diagnostic.Kind staticMemberDiagnosticKind; + private final Map provisionReports = new HashMap<>(); + private final Map membersInjectionReports = new HashMap<>(); - if (InjectionAnnotations.hasInjectAnnotation(constructorElement) - && constructorElement.hasAnnotation(TypeNames.ASSISTED_INJECT)) { - builder.addError("Constructors cannot be annotated with both @Inject and @AssistedInject"); + InternalValidator( + Diagnostic.Kind privateMemberDiagnosticKind, Diagnostic.Kind staticMemberDiagnosticKind) { + this.privateMemberDiagnosticKind = privateMemberDiagnosticKind; + this.staticMemberDiagnosticKind = staticMemberDiagnosticKind; } - ClassName injectAnnotation = - getAnyAnnotation( - constructorElement, - TypeNames.INJECT, - TypeNames.INJECT_JAVAX, - TypeNames.ASSISTED_INJECT) - .map(XAnnotations::getClassName) - .get(); - - if (constructorElement.isPrivate()) { - builder.addError( - "Dagger does not support injection into private constructors", constructorElement); + void clearCache() { + provisionReports.clear(); + membersInjectionReports.clear(); } - // If this type has already been processed in a previous round or compilation unit then there - // is no reason to recheck for invalid scope annotations since it's already been checked. - // This allows us to skip superficial validation of constructor annotations in subsequent - // compilations where the annotation types may no longer be on the classpath. - if (!processedInPreviousRoundOrCompilationUnit(constructorElement)) { - superficialValidation.validateAnnotationsOf(constructorElement); - for (XAnnotation qualifier : injectionAnnotations.getQualifiers(constructorElement)) { - builder.addError( - String.format( - "@Qualifier annotations are not allowed on @%s constructors", - injectAnnotation.simpleName()), - constructorElement, - qualifier); + ValidationReport validate(XTypeElement typeElement) { + return reentrantComputeIfAbsent(provisionReports, typeElement, this::validateUncached); + } + + private ValidationReport validateUncached(XTypeElement typeElement) { + ValidationReport.Builder builder = ValidationReport.about(typeElement); + builder.addSubreport(validateForMembersInjectionInternal(typeElement)); + + ImmutableSet injectConstructors = + ImmutableSet.builder() + .addAll(injectedConstructors(typeElement)) + .addAll(assistedInjectedConstructors(typeElement)) + .build(); + + switch (injectConstructors.size()) { + case 0: + break; // Nothing to validate. + case 1: + builder.addSubreport(validateConstructor(getOnlyElement(injectConstructors))); + break; + default: + builder.addError( + String.format( + "Type %s may only contain one injected constructor. Found: %s", + typeElement.getQualifiedName(), + injectConstructors.stream() + .map(methodSignatureFormatter::format) + .collect(toImmutableList())), + typeElement); } - String scopeErrorMsg = - String.format( - "@Scope annotations are not allowed on @%s constructors", - injectAnnotation.simpleName()); + return builder.build(); + } + + private ValidationReport validateConstructor(XConstructorElement constructorElement) { + superficialValidation.validateTypeOf(constructorElement); + ValidationReport.Builder builder = + ValidationReport.about(constructorElement.getEnclosingElement()); - if (injectAnnotation.equals(TypeNames.INJECT) - || injectAnnotation.equals(TypeNames.INJECT_JAVAX)) { - scopeErrorMsg += "; annotate the class instead"; + if (InjectionAnnotations.hasInjectAnnotation(constructorElement) + && constructorElement.hasAnnotation(TypeNames.ASSISTED_INJECT)) { + builder.addError("Constructors cannot be annotated with both @Inject and @AssistedInject"); } - for (Scope scope : injectionAnnotations.getScopes(constructorElement)) { - builder.addError(scopeErrorMsg, constructorElement, scope.scopeAnnotation().xprocessing()); + ClassName injectAnnotation = + getAnyAnnotation( + constructorElement, + TypeNames.INJECT, + TypeNames.INJECT_JAVAX, + TypeNames.ASSISTED_INJECT) + .map(XAnnotations::getClassName) + .get(); + + if (constructorElement.isPrivate()) { + builder.addError( + "Dagger does not support injection into private constructors", constructorElement); } - } - for (XExecutableParameterElement parameter : constructorElement.getParameters()) { - superficialValidation.validateTypeOf(parameter); - validateDependencyRequest(builder, parameter); - } + // If this type has already been processed in a previous round or compilation unit then there + // is no reason to recheck for invalid scope annotations since it's already been checked. + // This allows us to skip superficial validation of constructor annotations in subsequent + // compilations where the annotation types may no longer be on the classpath. + if (!processedInPreviousRoundOrCompilationUnit(constructorElement)) { + superficialValidation.validateAnnotationsOf(constructorElement); + for (XAnnotation qualifier : injectionAnnotations.getQualifiers(constructorElement)) { + builder.addError( + String.format( + "@Qualifier annotations are not allowed on @%s constructors", + injectAnnotation.simpleName()), + constructorElement, + qualifier); + } - if (throwsCheckedExceptions(constructorElement)) { - builder.addItem( - String.format( - "Dagger does not support checked exceptions on @%s constructors", - injectAnnotation.simpleName()), - privateMemberDiagnosticKind(), - constructorElement); - } + String scopeErrorMsg = + String.format( + "@Scope annotations are not allowed on @%s constructors", + injectAnnotation.simpleName()); - checkInjectIntoPrivateClass(constructorElement, builder); + if (injectAnnotation.equals(TypeNames.INJECT) + || injectAnnotation.equals(TypeNames.INJECT_JAVAX)) { + scopeErrorMsg += "; annotate the class instead"; + } - XTypeElement enclosingElement = constructorElement.getEnclosingElement(); - if (enclosingElement.isAbstract()) { - builder.addError( - String.format( - "@%s is nonsense on the constructor of an abstract class", - injectAnnotation.simpleName()), - constructorElement); - } + for (Scope scope : injectionAnnotations.getScopes(constructorElement)) { + builder.addError( + scopeErrorMsg, constructorElement, scope.scopeAnnotation().xprocessing()); + } + } - if (enclosingElement.isNested() && !enclosingElement.isStatic()) { - builder.addError( - String.format( - "@%s constructors are invalid on inner classes. " - + "Did you mean to make the class static?", - injectAnnotation.simpleName()), - constructorElement); - } + for (XExecutableParameterElement parameter : constructorElement.getParameters()) { + superficialValidation.validateTypeOf(parameter); + validateDependencyRequest(builder, parameter); + } - // Note: superficial validation of the annotations is done as part of getting the scopes. - ImmutableSet scopes = - injectionAnnotations.getScopes(constructorElement.getEnclosingElement()); - if (injectAnnotation.equals(TypeNames.ASSISTED_INJECT)) { - for (Scope scope : scopes) { + if (throwsCheckedExceptions(constructorElement)) { + builder.addItem( + String.format( + "Dagger does not support checked exceptions on @%s constructors", + injectAnnotation.simpleName()), + privateMemberDiagnosticKind, + constructorElement); + } + + checkInjectIntoPrivateClass(constructorElement, builder); + + XTypeElement enclosingElement = constructorElement.getEnclosingElement(); + if (enclosingElement.isAbstract()) { builder.addError( - "A type with an @AssistedInject-annotated constructor cannot be scoped", - enclosingElement, - scope.scopeAnnotation().xprocessing()); + String.format( + "@%s is nonsense on the constructor of an abstract class", + injectAnnotation.simpleName()), + constructorElement); } - } else if (scopes.size() > 1) { - for (Scope scope : scopes) { + + if (enclosingElement.isNested() && !enclosingElement.isStatic()) { builder.addError( - "A single binding may not declare more than one @Scope", - enclosingElement, - scope.scopeAnnotation().xprocessing()); + String.format( + "@%s constructors are invalid on inner classes. " + + "Did you mean to make the class static?", + injectAnnotation.simpleName()), + constructorElement); } - } - return builder.build(); - } + // Note: superficial validation of the annotations is done as part of getting the scopes. + ImmutableSet scopes = + injectionAnnotations.getScopes(constructorElement.getEnclosingElement()); + if (injectAnnotation.equals(TypeNames.ASSISTED_INJECT)) { + for (Scope scope : scopes) { + builder.addError( + "A type with an @AssistedInject-annotated constructor cannot be scoped", + enclosingElement, + scope.scopeAnnotation().xprocessing()); + } + } else if (scopes.size() > 1) { + for (Scope scope : scopes) { + builder.addError( + "A single binding may not declare more than one @Scope", + enclosingElement, + scope.scopeAnnotation().xprocessing()); + } + } - private ValidationReport validateField(XFieldElement fieldElement) { - superficialValidation.validateTypeOf(fieldElement); - ValidationReport.Builder builder = ValidationReport.about(fieldElement); - if (fieldElement.isFinal()) { - builder.addError("@Inject fields may not be final", fieldElement); + return builder.build(); } - if (fieldElement.isPrivate()) { - builder.addItem( - "Dagger does not support injection into private fields", - privateMemberDiagnosticKind(), - fieldElement); - } + private ValidationReport validateField(XFieldElement fieldElement) { + superficialValidation.validateTypeOf(fieldElement); + ValidationReport.Builder builder = ValidationReport.about(fieldElement); + if (fieldElement.isFinal()) { + builder.addError("@Inject fields may not be final", fieldElement); + } - if (fieldElement.isStatic()) { - builder.addItem( - "Dagger does not support injection into static fields", - staticMemberDiagnosticKind(), - fieldElement); - } + if (fieldElement.isPrivate()) { + builder.addItem( + "Dagger does not support injection into private fields", + privateMemberDiagnosticKind, + fieldElement); + } - if (fieldElement.isProtected() - && fieldElement.getEnclosingElement().isFromKotlin() - ) { - builder.addError( - "Dagger injector does not have access to kotlin protected fields", fieldElement); - } + if (fieldElement.isStatic()) { + builder.addItem( + "Dagger does not support injection into static fields", + staticMemberDiagnosticKind, + fieldElement); + } - validateDependencyRequest(builder, fieldElement); + if (fieldElement.isProtected() + && fieldElement.getEnclosingElement().isFromKotlin() + ) { + builder.addError( + "Dagger injector does not have access to kotlin protected fields", fieldElement); + } - return builder.build(); - } + validateDependencyRequest(builder, fieldElement); - private ValidationReport validateMethod(XMethodElement methodElement) { - superficialValidation.validateTypeOf(methodElement); - ValidationReport.Builder builder = ValidationReport.about(methodElement); - if (methodElement.isAbstract()) { - builder.addError("Methods with @Inject may not be abstract", methodElement); + return builder.build(); } - if (methodElement.isPrivate()) { - builder.addItem( - "Dagger does not support injection into private methods", - privateMemberDiagnosticKind(), - methodElement); - } + private ValidationReport validateMethod(XMethodElement methodElement) { + superficialValidation.validateTypeOf(methodElement); + ValidationReport.Builder builder = ValidationReport.about(methodElement); + if (methodElement.isAbstract()) { + builder.addError("Methods with @Inject may not be abstract", methodElement); + } - if (methodElement.isStatic()) { - builder.addItem( - "Dagger does not support injection into static methods", - staticMemberDiagnosticKind(), - methodElement); - } + if (methodElement.isPrivate()) { + builder.addItem( + "Dagger does not support injection into private methods", + privateMemberDiagnosticKind, + methodElement); + } - // No need to resolve type parameters since we're only checking existence. - if (hasTypeParameters(methodElement)) { - builder.addError("Methods with @Inject may not declare type parameters", methodElement); - } + if (methodElement.isStatic()) { + builder.addItem( + "Dagger does not support injection into static methods", + staticMemberDiagnosticKind, + methodElement); + } - // No need to resolve thrown types since we're only checking existence. - if (!methodElement.getThrownTypes().isEmpty()) { - builder.addError( - "Methods with @Inject may not throw checked exceptions. " - + "Please wrap your exceptions in a RuntimeException instead.", - methodElement); - } + // No need to resolve type parameters since we're only checking existence. + if (hasTypeParameters(methodElement)) { + builder.addError("Methods with @Inject may not declare type parameters", methodElement); + } - for (XExecutableParameterElement parameter : methodElement.getParameters()) { - superficialValidation.validateTypeOf(parameter); - validateDependencyRequest(builder, parameter); - } + // No need to resolve thrown types since we're only checking existence. + if (!methodElement.getThrownTypes().isEmpty()) { + builder.addError( + "Methods with @Inject may not throw checked exceptions. " + + "Please wrap your exceptions in a RuntimeException instead.", + methodElement); + } - return builder.build(); - } + for (XExecutableParameterElement parameter : methodElement.getParameters()) { + superficialValidation.validateTypeOf(parameter); + validateDependencyRequest(builder, parameter); + } - private void validateDependencyRequest( - ValidationReport.Builder builder, XVariableElement parameter) { - dependencyRequestValidator.validateDependencyRequest(builder, parameter, parameter.getType()); - dependencyRequestValidator.checkNotProducer(builder, parameter); - } + return builder.build(); + } - public ValidationReport validateForMembersInjection(XTypeElement typeElement) { - return !processedInPreviousRoundOrCompilationUnit(typeElement) - ? validate(typeElement) // validate everything - : validateForMembersInjectionInternal(typeElement); // validate only inject members - } + private void validateDependencyRequest( + ValidationReport.Builder builder, XVariableElement parameter) { + dependencyRequestValidator.validateDependencyRequest(builder, parameter, parameter.getType()); + dependencyRequestValidator.checkNotProducer(builder, parameter); + } - private ValidationReport validateForMembersInjectionInternal(XTypeElement typeElement) { - return reentrantComputeIfAbsent( - membersInjectionReports, typeElement, this::validateForMembersInjectionInternalUncached); - } + public ValidationReport validateForMembersInjection(XTypeElement typeElement) { + return !processedInPreviousRoundOrCompilationUnit(typeElement) + ? validate(typeElement) // validate everything + : validateForMembersInjectionInternal(typeElement); // validate only inject members + } + + private ValidationReport validateForMembersInjectionInternal(XTypeElement typeElement) { + return reentrantComputeIfAbsent( + membersInjectionReports, typeElement, this::validateForMembersInjectionInternalUncached); + } - private ValidationReport validateForMembersInjectionInternalUncached(XTypeElement typeElement) { - superficialValidation.validateTypeOf(typeElement); - // TODO(beder): This element might not be currently compiled, so this error message could be - // left in limbo. Find an appropriate way to display the error message in that case. - ValidationReport.Builder builder = ValidationReport.about(typeElement); - boolean hasInjectedMembers = false; - for (XFieldElement field : typeElement.getDeclaredFields()) { - if (InjectionAnnotations.hasInjectAnnotation(field)) { - hasInjectedMembers = true; - ValidationReport report = validateField(field); - if (!report.isClean()) { - builder.addSubreport(report); + private ValidationReport validateForMembersInjectionInternalUncached(XTypeElement typeElement) { + superficialValidation.validateTypeOf(typeElement); + // TODO(beder): This element might not be currently compiled, so this error message could be + // left in limbo. Find an appropriate way to display the error message in that case. + ValidationReport.Builder builder = ValidationReport.about(typeElement); + boolean hasInjectedMembers = false; + for (XFieldElement field : typeElement.getDeclaredFields()) { + if (InjectionAnnotations.hasInjectAnnotation(field)) { + hasInjectedMembers = true; + ValidationReport report = validateField(field); + if (!report.isClean()) { + builder.addSubreport(report); + } } } - } - for (XMethodElement method : typeElement.getDeclaredMethods()) { - if (InjectionAnnotations.hasInjectAnnotation(method)) { - hasInjectedMembers = true; - ValidationReport report = validateMethod(method); - if (!report.isClean()) { - builder.addSubreport(report); + for (XMethodElement method : typeElement.getDeclaredMethods()) { + if (InjectionAnnotations.hasInjectAnnotation(method)) { + hasInjectedMembers = true; + ValidationReport report = validateMethod(method); + if (!report.isClean()) { + builder.addSubreport(report); + } } } - } - - if (hasInjectedMembers) { - checkInjectIntoPrivateClass(typeElement, builder); - checkInjectIntoKotlinObject(typeElement, builder); - } - - Optional.ofNullable(typeElement.getSuperType()) - .filter(supertype -> !supertype.getTypeName().equals(TypeName.OBJECT)) - .ifPresent( - supertype -> { - superficialValidation.validateSuperTypeOf(typeElement); - ValidationReport report = validateForMembersInjection(supertype.getTypeElement()); - if (!report.isClean()) { - builder.addSubreport(report); - } - }); - - return builder.build(); - } - /** Returns true if the given method element declares a checked exception. */ - private boolean throwsCheckedExceptions(XConstructorElement constructorElement) { - XType runtimeException = processingEnv.findType(TypeNames.RUNTIME_EXCEPTION); - XType error = processingEnv.findType(TypeNames.ERROR); - superficialValidation.validateThrownTypesOf(constructorElement); - return !constructorElement.getThrownTypes().stream() - .allMatch(type -> isSubtype(type, runtimeException) || isSubtype(type, error)); - } + if (hasInjectedMembers) { + checkInjectIntoPrivateClass(typeElement, builder); + checkInjectIntoKotlinObject(typeElement, builder); + } - private void checkInjectIntoPrivateClass(XElement element, ValidationReport.Builder builder) { - if (!Accessibility.isElementAccessibleFromOwnPackage(closestEnclosingTypeElement(element))) { - builder.addItem( - "Dagger does not support injection into private classes", - privateMemberDiagnosticKind(), - element); + Optional.ofNullable(typeElement.getSuperType()) + .filter(supertype -> !supertype.getTypeName().equals(TypeName.OBJECT)) + .ifPresent( + supertype -> { + superficialValidation.validateSuperTypeOf(typeElement); + ValidationReport report = validateForMembersInjection(supertype.getTypeElement()); + if (!report.isClean()) { + builder.addSubreport(report); + } + }); + + return builder.build(); } - } - private void checkInjectIntoKotlinObject(XTypeElement element, ValidationReport.Builder builder) { - if (element.isKotlinObject() || element.isCompanionObject()) { - builder.addError("Dagger does not support injection into Kotlin objects", element); + /** Returns true if the given method element declares a checked exception. */ + private boolean throwsCheckedExceptions(XConstructorElement constructorElement) { + XType runtimeException = processingEnv.findType(TypeNames.RUNTIME_EXCEPTION); + XType error = processingEnv.findType(TypeNames.ERROR); + superficialValidation.validateThrownTypesOf(constructorElement); + return !constructorElement.getThrownTypes().stream() + .allMatch(type -> isSubtype(type, runtimeException) || isSubtype(type, error)); } - } - private Diagnostic.Kind privateMemberDiagnosticKind() { - return privateAndStaticInjectionDiagnosticKind.orElse( - compilerOptions.privateMemberValidationKind()); - } + private void checkInjectIntoPrivateClass(XElement element, ValidationReport.Builder builder) { + if (!Accessibility.isElementAccessibleFromOwnPackage(closestEnclosingTypeElement(element))) { + builder.addItem( + "Dagger does not support injection into private classes", + privateMemberDiagnosticKind, + element); + } + } - private Diagnostic.Kind staticMemberDiagnosticKind() { - return privateAndStaticInjectionDiagnosticKind.orElse( - compilerOptions.staticMemberValidationKind()); - } + private void checkInjectIntoKotlinObject( + XTypeElement element, ValidationReport.Builder builder) { + if (element.isKotlinObject() || element.isCompanionObject()) { + builder.addError("Dagger does not support injection into Kotlin objects", element); + } + } - private boolean processedInPreviousRoundOrCompilationUnit(XConstructorElement injectConstructor) { - return processingEnv.findTypeElement(factoryNameForElement(injectConstructor)) != null; - } + private boolean processedInPreviousRoundOrCompilationUnit( + XConstructorElement injectConstructor) { + return processingEnv.findTypeElement(factoryNameForElement(injectConstructor)) != null; + } - private boolean processedInPreviousRoundOrCompilationUnit(XTypeElement membersInjectedType) { - return processingEnv.findTypeElement(membersInjectorNameForType(membersInjectedType)) != null; + private boolean processedInPreviousRoundOrCompilationUnit(XTypeElement membersInjectedType) { + return processingEnv.findTypeElement(membersInjectorNameForType(membersInjectedType)) != null; + } } }