diff options
author | James Lemieux <jplemieux@google.com> | 2018-11-15 11:45:03 -0800 |
---|---|---|
committer | James Lemieux <jplemieux@google.com> | 2018-11-20 15:34:26 -0800 |
commit | 292ab7fbe0f25581df5026c4cb52b20cf027ccae (patch) | |
tree | dd92a999d64ceae6927c63d31af83ade622dc82b /processor | |
parent | 5a855d3384a6c91d944f42b97bcdee55a70dde85 (diff) | |
download | robolectric-shadows-292ab7fbe0f25581df5026c4cb52b20cf027ccae.tar.gz |
Merge robolectric/master up to commit ee67bd2
Bug: 116278591
Test: make -j56 Run_all_robolectric_tests
Change-Id: I0d1b044b7edd15774520985b1ff3c4301465c128
Diffstat (limited to 'processor')
3 files changed, 128 insertions, 63 deletions
diff --git a/processor/build.gradle b/processor/build.gradle index e74588dc1..ebdb4b780 100644 --- a/processor/build.gradle +++ b/processor/build.gradle @@ -1,7 +1,3 @@ -plugins { - id "net.ltgt.errorprone" version "0.0.13" -} - new RoboJavaModulePlugin( deploy: true ).apply(project) @@ -35,30 +31,18 @@ task('generateSdksFile', type: GenerateSdksFileTask) { tasks['classes'].dependsOn(generateSdksFile) -// Disable annotation processor for tests -compileTestJava { - options.compilerArgs.add("-proc:none") -} - dependencies { - // Project dependencies - compile project(":annotations") + implementation project(":annotations") - // Compile dependencies - compile "com.google.guava:guava:20.0" - compileOnly "com.google.code.findbugs:jsr305:3.0.1" - compile "com.google.code.gson:gson:2.8.0" - compile 'ch.raffael.pegdown-doclet:pegdown-doclet:1.3' + compileOnly "com.google.code.findbugs:jsr305:3.0.2" + implementation "com.google.guava:guava:20.0" + implementation "com.google.code.gson:gson:2.8.2" + implementation 'ch.raffael.pegdown-doclet:pegdown-doclet:1.3' - // in jdk 9, tools.jar disappears! - def toolsJar = org.gradle.internal.jvm.Jvm.current().getToolsJar() - if (toolsJar != null) { - compile files(toolsJar) - } + implementation files(org.gradle.internal.jvm.Jvm.current().getToolsJar()) - // Testing dependencies - testCompile "junit:junit:4.12" - testCompile "org.mockito:mockito-core:2.5.4" - testCompile "com.google.testing.compile:compile-testing:0.15" - testCompile "com.google.truth:truth:0.42" + testImplementation "junit:junit:4.12" + testImplementation "org.mockito:mockito-core:2.5.4" + testImplementation "com.google.testing.compile:compile-testing:0.15" + testImplementation "com.google.truth:truth:0.42" } diff --git a/processor/src/main/java/org/robolectric/annotation/processing/validator/ImplementsValidator.java b/processor/src/main/java/org/robolectric/annotation/processing/validator/ImplementsValidator.java index f9a790ccc..ee92a7f2e 100644 --- a/processor/src/main/java/org/robolectric/annotation/processing/validator/ImplementsValidator.java +++ b/processor/src/main/java/org/robolectric/annotation/processing/validator/ImplementsValidator.java @@ -66,13 +66,7 @@ public class ImplementsValidator extends Validator { private TypeElement getClassNameTypeElement(AnnotationValue cv) { String className = Helpers.getAnnotationStringValue(cv); - TypeElement type = elements.getTypeElement(className.replace('$', '.')); - - if (type == null) { - error("@Implements: could not resolve class <" + className + '>', cv); - return null; - } - return type; + return elements.getTypeElement(className.replace('$', '.')); } @Override @@ -104,17 +98,7 @@ public class ImplementsValidator extends Validator { // This shadow doesn't apply to the current SDK. todo: check each SDK. if (maxSdk != -1 && maxSdk < MAX_SUPPORTED_ANDROID_SDK) { - String sdkClassName; - if (av == null) { - sdkClassName = Helpers.getAnnotationStringValue(cv).replace('$', '.'); - } else { - sdkClassName = av.toString(); - } - - // there's no such type at the current SDK level, so just use strings... - // getQualifiedName() uses Outer.Inner and we want Outer$Inner, so: - String name = getClassFQName(shadowType); - modelBuilder.addExtraShadow(sdkClassName, name); + addShadowNotInSdk(shadowType, av, cv); return null; } @@ -125,6 +109,12 @@ public class ImplementsValidator extends Validator { return null; } actualType = getClassNameTypeElement(cv); + + if (actualType == null + && !suppressWarnings(shadowType, "robolectric.internal.IgnoreMissingClass")) { + error("@Implements: could not resolve class <" + cv + '>', cv); + return null; + } } else { TypeMirror value = Helpers.getAnnotationTypeMirrorValue(av); if (value == null) { @@ -137,6 +127,7 @@ public class ImplementsValidator extends Validator { } } if (actualType == null) { + addShadowNotInSdk(shadowType, av, cv); return null; } final List<? extends TypeParameterElement> typeTP = actualType.getTypeParameters(); @@ -173,6 +164,32 @@ public class ImplementsValidator extends Validator { return null; } + private void addShadowNotInSdk(TypeElement shadowType, AnnotationValue av, AnnotationValue cv) { + String sdkClassName; + if (av == null) { + sdkClassName = Helpers.getAnnotationStringValue(cv).replace('$', '.'); + } else { + sdkClassName = av.toString(); + } + + // there's no such type at the current SDK level, so just use strings... + // getQualifiedName() uses Outer.Inner and we want Outer$Inner, so: + String name = getClassFQName(shadowType); + modelBuilder.addExtraShadow(sdkClassName, name); + } + + private static boolean suppressWarnings(Element element, String warningName) { + SuppressWarnings[] suppressWarnings = element.getAnnotationsByType(SuppressWarnings.class); + for (SuppressWarnings suppression : suppressWarnings) { + for (String name : suppression.value()) { + if (warningName.equals(name)) { + return true; + } + } + } + return false; + } + static String getClassFQName(TypeElement elem) { StringBuilder name = new StringBuilder(); while (isClassy(elem.getEnclosingElement().getKind())) { diff --git a/processor/src/main/java/org/robolectric/annotation/processing/validator/SdkStore.java b/processor/src/main/java/org/robolectric/annotation/processing/validator/SdkStore.java index c65076e78..e01674dba 100644 --- a/processor/src/main/java/org/robolectric/annotation/processing/validator/SdkStore.java +++ b/processor/src/main/java/org/robolectric/annotation/processing/validator/SdkStore.java @@ -99,6 +99,20 @@ class SdkStore { } } + private static String canonicalize(TypeMirror typeMirror) { + if (typeMirror instanceof TypeVar) { + return ((TypeVar) typeMirror).getUpperBound().toString(); + } else if (typeMirror instanceof ArrayType) { + return canonicalize(((ArrayType) typeMirror).elemtype) + "[]"; + } else { + return typeMirror.toString(); + } + } + + private static String typeWithoutGenerics(String paramType) { + return paramType.replaceAll("<.*", ""); + } + static class Sdk implements Comparable<Sdk> { private static final ClassInfo NULL_CLASS_INFO = new ClassInfo(); @@ -138,22 +152,61 @@ class SdkStore { } MethodExtraInfo implMethod = new MethodExtraInfo(methodElement); - if (sdkMethod.equals(implMethod)) { + if (!sdkMethod.equals(implMethod) + && !suppressWarnings(methodElement, "robolectric.ShadowReturnTypeMismatch")) { if (implMethod.isStatic != sdkMethod.isStatic) { return "@Implementation for " + methodElement.getSimpleName() + " is " + (implMethod.isStatic ? "static" : "not static") + " unlike the SDK method"; } if (!implMethod.returnType.equals(sdkMethod.returnType)) { - return "@Implementation for " + methodElement.getSimpleName() - + " has a return type of " + implMethod.returnType - + ", not " + sdkMethod.returnType + " as in the SDK method"; + if ( + (looseSignatures && typeIsOkForLooseSignatures(implMethod, sdkMethod)) + || (looseSignatures && implMethod.returnType.equals("java.lang.Object[]")) + // Number is allowed for int or long return types + || typeIsNumeric(sdkMethod, implMethod)) { + return null; + } else { + return "@Implementation for " + methodElement.getSimpleName() + + " has a return type of " + implMethod.returnType + + ", not " + sdkMethod.returnType + " as in the SDK method"; + } } } return null; } + private boolean suppressWarnings(ExecutableElement methodElement, String warningName) { + SuppressWarnings[] suppressWarnings = methodElement.getAnnotationsByType(SuppressWarnings.class); + for (SuppressWarnings suppression : suppressWarnings) { + for (String name : suppression.value()) { + if (warningName.equals(name)) { + return true; + } + } + } + return false; + } + + private boolean typeIsNumeric(MethodExtraInfo sdkMethod, MethodExtraInfo implMethod) { + return implMethod.returnType.equals("java.lang.Number") + && isNumericType(sdkMethod.returnType); + } + + private boolean typeIsOkForLooseSignatures(MethodExtraInfo implMethod, MethodExtraInfo sdkMethod) { + return + // loose signatures allow a return type of Object... + implMethod.returnType.equals("java.lang.Object") + // or Object[] for arrays... + || (implMethod.returnType.equals("java.lang.Object[]") + && sdkMethod.returnType.endsWith("[]")); + } + + private boolean isNumericType(String type) { + return type.equals("int") || type.equals("long"); + } + /** * Load and analyze bytecode for the specified class, with caching. * @@ -293,7 +346,7 @@ class SdkStore { public MethodInfo(MethodNode method) { this.name = method.name; for (Type type : Type.getArgumentTypes(method.desc)) { - paramTypes.add(type.getClassName().replace('$', '.')); + paramTypes.add(normalize(type)); } } @@ -312,21 +365,11 @@ class SdkStore { for (VariableElement variableElement : methodElement.getParameters()) { TypeMirror varTypeMirror = variableElement.asType(); String paramType = canonicalize(varTypeMirror); - String paramTypeWithoutGenerics = paramType.replaceAll("<.*", ""); + String paramTypeWithoutGenerics = typeWithoutGenerics(paramType); paramTypes.add(paramTypeWithoutGenerics); } } - private String canonicalize(TypeMirror typeMirror) { - if (typeMirror instanceof TypeVar) { - return ((TypeVar) typeMirror).getUpperBound().toString(); - } else if (typeMirror instanceof ArrayType) { - return canonicalize(((ArrayType) typeMirror).elemtype) + "[]"; - } else { - return typeMirror.toString(); - } - } - private String cleanMethodName(ExecutableElement methodElement) { String name = methodElement.getSimpleName().toString(); if (CONSTRUCTOR_METHOD_NAME.equals(name)) { @@ -359,7 +402,6 @@ class SdkStore { public int hashCode() { return Objects.hash(name, paramTypes); } - @Override public String toString() { return "MethodInfo{" @@ -369,18 +411,40 @@ class SdkStore { } } + private static String normalize(Type type) { + return type.getClassName().replace('$', '.'); + } + static class MethodExtraInfo { private final boolean isStatic; private final String returnType; public MethodExtraInfo(MethodNode method) { this.isStatic = (method.access & Opcodes.ACC_STATIC) != 0; - this.returnType = Type.getReturnType(method.desc).getClassName(); + this.returnType = typeWithoutGenerics(normalize(Type.getReturnType(method.desc))); } public MethodExtraInfo(ExecutableElement methodElement) { this.isStatic = methodElement.getModifiers().contains(Modifier.STATIC); - this.returnType = methodElement.getReturnType().toString(); + this.returnType = typeWithoutGenerics(canonicalize(methodElement.getReturnType())); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + MethodExtraInfo that = (MethodExtraInfo) o; + return isStatic == that.isStatic && + Objects.equals(returnType, that.returnType); + } + + @Override + public int hashCode() { + return Objects.hash(isStatic, returnType); } } } |