aboutsummaryrefslogtreecommitdiff
path: root/processor
diff options
context:
space:
mode:
authorJames Lemieux <jplemieux@google.com>2018-11-15 11:45:03 -0800
committerJames Lemieux <jplemieux@google.com>2018-11-20 15:34:26 -0800
commit292ab7fbe0f25581df5026c4cb52b20cf027ccae (patch)
treedd92a999d64ceae6927c63d31af83ade622dc82b /processor
parent5a855d3384a6c91d944f42b97bcdee55a70dde85 (diff)
downloadrobolectric-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')
-rw-r--r--processor/build.gradle36
-rw-r--r--processor/src/main/java/org/robolectric/annotation/processing/validator/ImplementsValidator.java53
-rw-r--r--processor/src/main/java/org/robolectric/annotation/processing/validator/SdkStore.java102
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);
}
}
}