Skip to content

Commit

Permalink
Add support for AndroidX annotations.
Browse files Browse the repository at this point in the history
  • Loading branch information
mik9 authored and KengoTODA committed Feb 20, 2019
1 parent e7ec9d8 commit ac77680
Show file tree
Hide file tree
Showing 10 changed files with 181 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -6,6 +6,10 @@ Currently the versioning policy of this project follows [Semantic Versioning v2.

## Unreleased - 2019-??-??

### Added

* Make TypeQualifierResolver recognize androidx.annotation.NonNull and Nullable ([#880](https://github.com/spotbugs/spotbugs/pull/880))

### Changed
* Bump up Apache Commons BCEL to [the version 6.3](http://mail-archives.apache.org/mod_mbox/commons-user/201901.mbox/%3CCACZkXPy3VgLmD2jppzEPwOqVDJYMM2QG%2BtWQCyzfKmZrDwem6A%40mail.gmail.com%3E)

Expand Down
Expand Up @@ -30,6 +30,13 @@ public void objectForNonNullParam_isOk() {
assertThat(bugCollection, emptyIterable());
}

@Test
public void objectForNonNullParam2_isOk() {
BugCollection bugCollection = spotbugs.performAnalysis(
Paths.get("../spotbugsTestCases/build/classes/java/main/androidAnnotations/ObjectForNonNullParam2.class"));
assertThat(bugCollection, emptyIterable());
}

@Test
public void nullForNonNullParam_isDetected() {
BugCollection bugCollection = spotbugs.performAnalysis(
Expand All @@ -38,13 +45,28 @@ public void nullForNonNullParam_isDetected() {
assertThat(bugCollection, containsExactly(1, bug("NP_NONNULL_PARAM_VIOLATION")));
}

@Test
public void nullForNonNullParam2_isDetected() {
BugCollection bugCollection = spotbugs.performAnalysis(
Paths.get("../spotbugsTestCases/build/classes/java/main/androidAnnotations/NullForNonNullParam2.class"));

assertThat(bugCollection, containsExactly(1, bug("NP_NONNULL_PARAM_VIOLATION")));
}

@Test
public void checkedNullableReturn_isOk() {
BugCollection bugCollection = spotbugs.performAnalysis(
Paths.get("../spotbugsTestCases/build/classes/java/main/androidAnnotations/CheckedNullableReturn.class"));
assertThat(bugCollection, emptyIterable());
}

@Test
public void checkedNullableReturn2_isOk() {
BugCollection bugCollection = spotbugs.performAnalysis(
Paths.get("../spotbugsTestCases/build/classes/java/main/androidAnnotations/CheckedNullableReturn2.class"));
assertThat(bugCollection, emptyIterable());
}

@Test
public void uncheckedNullableReturn_isDetected() {
BugCollection bugCollection = spotbugs.performAnalysis(
Expand All @@ -53,6 +75,14 @@ public void uncheckedNullableReturn_isDetected() {
assertThat(bugCollection, containsExactly(1, bug("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")));
}

@Test
public void uncheckedNullableReturn2_isDetected() {
BugCollection bugCollection = spotbugs.performAnalysis(
Paths.get("../spotbugsTestCases/build/classes/java/main/androidAnnotations/UncheckedNullableReturn2.class"));

assertThat(bugCollection, containsExactly(1, bug("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")));
}

private BugInstanceMatcher bug(String type) {
return new BugInstanceMatcherBuilder().bugType(type).build();
}
Expand Down
Expand Up @@ -37,6 +37,7 @@ public class NullnessAnnotation extends AnnotationEnumeration<NullnessAnnotation
@Override
boolean match(@DottedClassName String className) {
return "android.support.annotation.Nullable".equals(className)
|| "androidx.annotation.Nullable".equals(className)
|| "com.google.common.base.Nullable".equals(className)
|| "org.eclipse.jdt.annotation.Nullable".equals(className)
|| "org.jetbrains.annotations.Nullable".equals(className)
Expand Down
Expand Up @@ -55,6 +55,10 @@ public class TypeQualifierResolver {

static final ClassDescriptor androidNonNull = DescriptorFactory.createClassDescriptor("android/support/annotation/NonNull");

static final ClassDescriptor androidxNullable = DescriptorFactory.createClassDescriptor("androidx/annotation/Nullable");

static final ClassDescriptor androidxNonNull = DescriptorFactory.createClassDescriptor("androidx/annotation/NonNull");

static final ClassDescriptor googleNullable = DescriptorFactory.createClassDescriptor("com/google/common/base/Nullable");

static final ClassDescriptor intellijNullable = DescriptorFactory.createClassDescriptor("org/jetbrains/annotations/Nullable");
Expand Down Expand Up @@ -133,6 +137,7 @@ private static void resolveTypeQualifierNicknames(AnnotationValue value, LinkedL

try {
if (annotationClass.equals(androidNullable)
|| annotationClass.equals(androidxNullable)
|| annotationClass.equals(googleNullable)
|| annotationClass.equals(eclipseNullable)
|| annotationClass.equals(intellijNullable)
Expand All @@ -142,6 +147,7 @@ private static void resolveTypeQualifierNicknames(AnnotationValue value, LinkedL
return;
}
if (annotationClass.equals(androidNonNull)
|| annotationClass.equals(androidxNonNull)
|| annotationClass.equals(eclipseNonNull)
|| annotationClass.equals(eclipseNonNullByDefault)
|| annotationClass.equals(intellijNotNull)) {
Expand Down
@@ -0,0 +1,39 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* 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 androidx.annotation;

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PACKAGE;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.CLASS;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
* Denotes that a parameter, field or method return value can never be null.
* <p>
* This is a marker annotation and it has no specific attributes.
*/
@Documented
@Retention(CLASS)
@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE})
public @interface NonNull {
}
@@ -0,0 +1,46 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* 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 androidx.annotation;

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PACKAGE;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.CLASS;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
* Denotes that a parameter, field or method return value can be null.
* <p>
* When decorating a method call parameter, this denotes that the parameter can
* legitimately be null and the method will gracefully deal with it. Typically
* used on optional parameters.
* <p>
* When decorating a method, this denotes the method might legitimately return
* null.
* <p>
* This is a marker annotation and it has no specific attributes.
*/
@Documented
@Retention(CLASS)
@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE})
public @interface Nullable {
}
@@ -0,0 +1,17 @@
package androidAnnotations;

import androidx.annotation.Nullable;

public class CheckedNullableReturn2 {
@Nullable
String foo() {
return null;
}

void bar() {
String foo = foo();
if (foo != null) {
System.out.println(foo.hashCode());
}
}
}
@@ -0,0 +1,12 @@
package androidAnnotations;

import androidx.annotation.NonNull;

public class NullForNonNullParam2 {
static void foo(@NonNull Object o) {
}

static void bar() {
foo(null);
}
}
@@ -0,0 +1,12 @@
package androidAnnotations;

import androidx.annotation.NonNull;

public class ObjectForNonNullParam2 {
static void foo(@NonNull Object o) {
}

static void bar() {
foo(new Object());
}
}
@@ -0,0 +1,14 @@
package androidAnnotations;

import androidx.annotation.Nullable;

public class UncheckedNullableReturn2 {
@Nullable
String foo() {
return null;
}

void bar() {
System.out.println(foo().hashCode());
}
}

0 comments on commit ac77680

Please sign in to comment.