Skip to content

Commit

Permalink
Make FragmentNotInstantiable accept additional Fragment classes.
Browse files Browse the repository at this point in the history
RELNOTES: FragmentNotInstantiable now accepts additional Fragment
classes.

MOE_MIGRATED_REVID=152717561
  • Loading branch information
jaslong authored and cushon committed Apr 10, 2017
1 parent 7668058 commit db8dfe0
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 16 deletions.
Expand Up @@ -28,6 +28,7 @@
import static javax.lang.model.element.Modifier.PUBLIC; import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.NestingKind.MEMBER; import static javax.lang.model.element.NestingKind.MEMBER;


import com.google.common.collect.ImmutableSet;
import com.google.errorprone.BugPattern; import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState; import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker; import com.google.errorprone.bugpatterns.BugChecker;
Expand All @@ -39,6 +40,7 @@
import com.sun.source.tree.MethodTree; import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree; import com.sun.source.tree.Tree;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;


/** @author avenet@google.com (Arnaud J. Venet) */ /** @author avenet@google.com (Arnaud J. Venet) */
@BugPattern( @BugPattern(
Expand All @@ -59,8 +61,29 @@ public class FragmentNotInstantiable extends BugChecker implements ClassTreeMatc
// platforms prior to Android 3.0. // platforms prior to Android 3.0.
private static final String FRAGMENT_CLASS_V4 = "android.support.v4.app.Fragment"; private static final String FRAGMENT_CLASS_V4 = "android.support.v4.app.Fragment";


private static final Matcher<ClassTree> FRAGMENT_MATCHER = private final ImmutableSet<String> fragmentClasses;
allOf(kindIs(CLASS), anyOf(isSubtypeOf(FRAGMENT_CLASS), isSubtypeOf(FRAGMENT_CLASS_V4))); private final Matcher<ClassTree> fragmentMatcher;

public FragmentNotInstantiable() {
this(ImmutableSet.of());
}

protected FragmentNotInstantiable(Iterable<String> additionalFragmentClasses) {
fragmentClasses =
ImmutableSet.<String>builder()
.add(FRAGMENT_CLASS)
.add(FRAGMENT_CLASS_V4)
.addAll(additionalFragmentClasses)
.build();
fragmentMatcher =
allOf(
kindIs(CLASS),
anyOf(
fragmentClasses
.stream()
.map(fragmentClass -> isSubtypeOf(fragmentClass))
.collect(Collectors.toList())));
}


private Description buildErrorMessage(Tree tree, String explanation) { private Description buildErrorMessage(Tree tree, String explanation) {
Description.Builder description = buildDescription(tree); Description.Builder description = buildDescription(tree);
Expand All @@ -70,12 +93,12 @@ private Description buildErrorMessage(Tree tree, String explanation) {


@Override @Override
public Description matchClass(ClassTree classTree, VisitorState state) { public Description matchClass(ClassTree classTree, VisitorState state) {
if (!FRAGMENT_MATCHER.matches(classTree, state)) { if (!fragmentMatcher.matches(classTree, state)) {
return Description.NO_MATCH; return Description.NO_MATCH;
} }


String className = ASTHelpers.getSymbol(classTree).toString(); String className = ASTHelpers.getSymbol(classTree).toString();
if (className.equals(FRAGMENT_CLASS) || className.equals(FRAGMENT_CLASS_V4)) { if (fragmentClasses.contains(className)) {
// Do not check the base class declarations (or their stubs). // Do not check the base class declarations (or their stubs).
return Description.NO_MATCH; return Description.NO_MATCH;
} }
Expand Down
Expand Up @@ -16,32 +16,68 @@


package com.google.errorprone.bugpatterns.android; package com.google.errorprone.bugpatterns.android;


import static com.google.errorprone.BugPattern.Category.ANDROID;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;

import com.google.common.collect.ImmutableSet;
import com.google.errorprone.BugPattern;
import com.google.errorprone.CompilationTestHelper; import com.google.errorprone.CompilationTestHelper;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.JUnit4; import org.junit.runners.JUnit4;


/** /** @author avenet@google.com (Arnaud J. Venet) */
* @author avenet@google.com (Arnaud J. Venet)
*/
@RunWith(JUnit4.class) @RunWith(JUnit4.class)
public class FragmentNotInstantiableTest { public class FragmentNotInstantiableTest {
private CompilationTestHelper compilationHelper; /** Used for testing a custom FragmentNotInstantiable. */

@BugPattern(
@Before name = "CustomFragmentNotInstantiable",
public void setUp() { summary =
compilationHelper = "Subclasses of CustomFragment must be instantiable via Class#newInstance():"
CompilationTestHelper.newInstance(FragmentNotInstantiable.class, getClass()); + " the class must be public, static and have a public nullary constructor",
category = ANDROID,
severity = WARNING
)
public static class CustomFragmentNotInstantiable extends FragmentNotInstantiable {
public CustomFragmentNotInstantiable() {
super(ImmutableSet.of("com.google.errorprone.bugpatterns.android.testdata.CustomFragment"));
}
} }


@Test @Test
public void testPositiveCases() throws Exception { public void testPositiveCases() throws Exception {
compilationHelper.addSourceFile("FragmentNotInstantiablePositiveCases.java").doTest(); createCompilationTestHelper(FragmentNotInstantiable.class)
.addSourceFile("FragmentNotInstantiablePositiveCases.java")
.doTest();
} }


@Test @Test
public void testNegativeCase() throws Exception { public void testNegativeCase() throws Exception {
compilationHelper.addSourceFile("FragmentNotInstantiableNegativeCases.java").doTest(); createCompilationTestHelper(FragmentNotInstantiable.class)
.addSourceFile("FragmentNotInstantiableNegativeCases.java")
.doTest();
}

@Test
public void testPositiveCases_custom() throws Exception {
createCompilationTestHelper(CustomFragmentNotInstantiable.class)
.addSourceFile("FragmentNotInstantiablePositiveCases.java")
.addSourceFile("CustomFragment.java")
.addSourceFile("CustomFragmentNotInstantiablePositiveCases.java")
.doTest();
}

@Test
public void testNegativeCase_custom() throws Exception {
createCompilationTestHelper(CustomFragmentNotInstantiable.class)
.addSourceFile("FragmentNotInstantiableNegativeCases.java")
.addSourceFile("CustomFragment.java")
.addSourceFile("CustomFragmentNotInstantiableNegativeCases.java")
.doTest();
}

private CompilationTestHelper createCompilationTestHelper(
Class<? extends FragmentNotInstantiable> bugCheckerClass) {
return CompilationTestHelper.newInstance(bugCheckerClass, getClass());
} }
} }
@@ -0,0 +1,20 @@
/*
* Copyright 2017 Google Inc. All Rights Reserved.
*
* 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 com.google.errorprone.bugpatterns.android.testdata;

/** @author jasonlong@google.com (Jason Long) */
public class CustomFragment {}
@@ -0,0 +1,72 @@
/*
* Copyright 2017 Google Inc. All Rights Reserved.
*
* 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 com.google.errorprone.bugpatterns.android.testdata;

/**
* @author jasonlong@google.com (Jason Long)
*/
public class CustomFragmentNotInstantiableNegativeCases {
public static class NotAFragment1 {
public NotAFragment1(int x) {}
}

public static class NotAFragment2 {
private NotAFragment2() {}
}

private static class NotAFragment3 {}

public class NotAFragment4 {}

private abstract class AbstractFragment extends CustomFragment {
public AbstractFragment(int x) {}
}

public static class MyFragment extends CustomFragment {
private int a;

public int value() {
return a;
}
}

public static class DerivedFragment extends MyFragment {}

public static class MyFragment2 extends CustomFragment {
public MyFragment2() {}

public MyFragment2(int x) {}
}

public static class DerivedFragment2 extends MyFragment2 {
public DerivedFragment2() {}

public DerivedFragment2(boolean b) {}
}

public static class EnclosingClass {
public static class InnerFragment extends CustomFragment {
public InnerFragment() {}
}
}

interface AnInterface {
public class ImplicitlyStaticInnerFragment extends CustomFragment {}

class ImplicitlyStaticAndPublicInnerFragment extends CustomFragment {}
}
}
@@ -0,0 +1,78 @@
/*
* Copyright 2017 Google Inc. All Rights Reserved.
*
* 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 com.google.errorprone.bugpatterns.android.testdata;

/**
* @author jasonlong@google.com (Jason Long)
*/
public class CustomFragmentNotInstantiablePositiveCases {
// BUG: Diagnostic contains: public
static class PrivateFragment extends CustomFragment {
public PrivateFragment() {}
}

public static class PrivateConstructor extends CustomFragment {
// BUG: Diagnostic contains: public
PrivateConstructor() {}
}

// BUG: Diagnostic contains: nullary constructor
public static class NoConstructor extends CustomFragment {
public NoConstructor(int x) {}
}

// BUG: Diagnostic contains: nullary constructor
public static class NoConstructorV4 extends android.support.v4.app.Fragment {
public NoConstructorV4(int x) {}
}

public static class ParentFragment extends CustomFragment {
public ParentFragment() {}
}

public static class ParentFragmentV4 extends android.support.v4.app.Fragment {
public ParentFragmentV4() {}
}

// BUG: Diagnostic contains: nullary constructor
public static class DerivedFragmentNoConstructor extends ParentFragment {
public DerivedFragmentNoConstructor(int x) {}
}

// BUG: Diagnostic contains: nullary constructor
public static class DerivedFragmentNoConstructorV4 extends ParentFragmentV4 {
public DerivedFragmentNoConstructorV4(boolean b) {}
}

public class EnclosingClass {
// BUG: Diagnostic contains: static
public class InnerFragment extends CustomFragment {
public InnerFragment() {}
}

public CustomFragment create1() {
// BUG: Diagnostic contains: public
return new CustomFragment() {};
}

public CustomFragment create2() {
// BUG: Diagnostic contains: public
class LocalFragment extends CustomFragment {}
return new LocalFragment();
}
}
}

0 comments on commit db8dfe0

Please sign in to comment.