diff --git a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseJavaLibraryPlugin.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseJavaLibraryPlugin.java index 9c507fa7536..2e2438a3fbc 100644 --- a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseJavaLibraryPlugin.java +++ b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseJavaLibraryPlugin.java @@ -68,13 +68,6 @@ private static void setupStaticAnalysis(Project project, FirebaseLibraryExtensio .getConfigurations() .all( c -> { - if ("annotationProcessor".equals(c.getName())) { - for (String checkProject : library.staticAnalysis.errorproneCheckProjects) { - project - .getDependencies() - .add("annotationProcessor", project.project(checkProject)); - } - } if ("lintChecks".equals(c.getName())) { for (String checkProject : library.staticAnalysis.androidLintCheckProjects) { diff --git a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseLibraryExtension.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseLibraryExtension.java index f934dbcdde2..559c7e2017e 100644 --- a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseLibraryExtension.java +++ b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseLibraryExtension.java @@ -91,7 +91,6 @@ public FirebaseLibraryExtension(Project project, LibraryType type) { private FirebaseStaticAnalysis initializeStaticAnalysis(Project project) { return new FirebaseStaticAnalysis( - projectsFromProperty(project, "firebase.checks.errorproneProjects"), projectsFromProperty(project, "firebase.checks.lintProjects")); } diff --git a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseLibraryPlugin.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseLibraryPlugin.java index 87374d14265..b4b9f89c77a 100644 --- a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseLibraryPlugin.java +++ b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseLibraryPlugin.java @@ -213,13 +213,6 @@ private static void setupStaticAnalysis(Project project, FirebaseLibraryExtensio .getConfigurations() .all( c -> { - if ("annotationProcessor".equals(c.getName())) { - for (String checkProject : library.staticAnalysis.errorproneCheckProjects) { - project - .getDependencies() - .add("annotationProcessor", project.project(checkProject)); - } - } if ("lintChecks".equals(c.getName())) { for (String checkProject : library.staticAnalysis.androidLintCheckProjects) { diff --git a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseStaticAnalysis.java b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseStaticAnalysis.java index 1f8da8b03b9..374a66f2fb3 100644 --- a/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseStaticAnalysis.java +++ b/buildSrc/src/main/java/com/google/firebase/gradle/plugins/FirebaseStaticAnalysis.java @@ -17,12 +17,9 @@ import java.util.Set; public class FirebaseStaticAnalysis { - public Set errorproneCheckProjects; public Set androidLintCheckProjects; - public FirebaseStaticAnalysis( - Set errorproneCheckProjects, Set androidLintCheckProjects) { - this.errorproneCheckProjects = errorproneCheckProjects; + public FirebaseStaticAnalysis(Set androidLintCheckProjects) { this.androidLintCheckProjects = androidLintCheckProjects; } } diff --git a/gradle.properties b/gradle.properties index 7508b2fb8ad..43b684aa426 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,7 +16,6 @@ org.gradle.jvmargs=-Xmx8g -XX:MaxPermSize=8g org.gradle.parallel=true org.gradle.caching=true -firebase.checks.errorproneProjects=:tools:errorprone firebase.checks.lintProjects=:tools:lint systemProp.illegal-access=warn diff --git a/subprojects.cfg b/subprojects.cfg index 1da5e643526..00c46f72e7b 100644 --- a/subprojects.cfg +++ b/subprojects.cfg @@ -67,7 +67,6 @@ encoders:protoc-gen-firebase-encoders:tests integ-testing -tools:errorprone tools:lint transport diff --git a/tools/errorprone/errorprone.gradle b/tools/errorprone/errorprone.gradle deleted file mode 100644 index f8be3518dbf..00000000000 --- a/tools/errorprone/errorprone.gradle +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2018 Google LLC -// -// 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. - -apply plugin: 'java-library' - - -dependencies { - implementation 'com.google.errorprone:error_prone_check_api:2.3.2' - implementation 'com.google.auto.service:auto-service:1.0-rc4' - annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4' - - testImplementation 'junit:junit:4.12' - testImplementation 'com.google.errorprone:error_prone_test_helpers:2.3.3' - testImplementation 'com.google.errorprone:javac:9+181-r4173-1' -} diff --git a/tools/errorprone/src/main/java/com/google/firebase/errorprone/ComponentsAppGetCheck.java b/tools/errorprone/src/main/java/com/google/firebase/errorprone/ComponentsAppGetCheck.java deleted file mode 100644 index b42c5dbfa48..00000000000 --- a/tools/errorprone/src/main/java/com/google/firebase/errorprone/ComponentsAppGetCheck.java +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2018 Google LLC -// -// 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.firebase.errorprone; - -import static com.google.errorprone.matchers.Matchers.allOf; -import static com.google.errorprone.matchers.Matchers.anyOf; -import static com.google.errorprone.matchers.Matchers.enclosingClass; -import static com.google.errorprone.matchers.Matchers.enclosingMethod; -import static com.google.errorprone.matchers.Matchers.hasAnnotation; -import static com.google.errorprone.matchers.Matchers.instanceMethod; -import static com.google.errorprone.matchers.Matchers.isStatic; -import static com.google.errorprone.matchers.Matchers.methodHasVisibility; -import static com.google.errorprone.matchers.Matchers.methodInvocation; -import static com.google.errorprone.matchers.Matchers.methodIsNamed; -import static com.google.errorprone.matchers.Matchers.methodReturns; -import static com.google.errorprone.util.ASTHelpers.getType; -import static com.google.errorprone.util.ASTHelpers.isSubtype; - -import com.google.auto.service.AutoService; -import com.google.errorprone.BugPattern; -import com.google.errorprone.VisitorState; -import com.google.errorprone.bugpatterns.BugChecker; -import com.google.errorprone.matchers.AbstractTypeMatcher; -import com.google.errorprone.matchers.Description; -import com.google.errorprone.matchers.Matcher; -import com.google.errorprone.matchers.MethodVisibility; -import com.google.errorprone.suppliers.Supplier; -import com.google.errorprone.util.ASTHelpers; -import com.sun.source.tree.ClassTree; -import com.sun.source.tree.ExpressionTree; -import com.sun.source.tree.MethodInvocationTree; -import com.sun.source.tree.Tree; -import com.sun.tools.javac.code.Type; - -/** - * Errorprone custom check that discourages use of FirebaseApp#get(Class) as it is only intended for - * use in Sdk#getInstance() methods. - */ -@BugPattern( - name = "FirebaseUseExplicitDependencies", - summary = - "Use of FirebaseApp#get(Class) is discouraged, and is only acceptable" - + " in SDK#getInstance(...) methods. Instead declare dependencies explicitly in" - + " your ComponentRegistrar and inject.", - severity = BugPattern.SeverityLevel.ERROR) -@AutoService(BugChecker.class) -public class ComponentsAppGetCheck extends BugChecker - implements BugChecker.MethodInvocationTreeMatcher { - private static final String FIREBASE_APP = "com.google.firebase.FirebaseApp"; - private static final String GET_COMPONENT_METHOD = "get(java.lang.Class)"; - - private static final Matcher CALL_TO_GET = - methodInvocation( - instanceMethod().onExactClass(FIREBASE_APP).withSignature(GET_COMPONENT_METHOD)); - - /** - * This matches methods of the forms: - * - *
{@code
-   * class Foo {
-   *     public static Foo getInstance(/* any number of parameters * /);
-   * }
-   *
-   * class Foo extends/implements Bar {
-   *     public static Bar getInstance(/* any number of parameters * /);
-   * }
-   * }
- */ - private static final Matcher WITHIN_GET_INSTANCE = - enclosingMethod( - allOf( - isStatic(), - methodIsNamed("getInstance"), - methodReturns( - isSupertypeOf( - state -> ASTHelpers.getType(state.findEnclosing(ClassTree.class)))))); - - /** - * This matches methods of the forms: - * - *
{@code
-   * class Foo {
-   *     private static Foo getInstanceImpl(/* any number of parameters * /);
-   * }
-   *
-   * class Foo extends/implements Bar {
-   *     private static Bar getInstanceImpl(/* any number of parameters * /);
-   * }
-   * }
- */ - private static final Matcher WITHIN_GET_INSTANCE_IMPL = - enclosingMethod( - allOf( - isStatic(), - methodHasVisibility(MethodVisibility.Visibility.PRIVATE), - methodIsNamed("getInstanceImpl"), - methodReturns( - isSupertypeOf( - state -> ASTHelpers.getType(state.findEnclosing(ClassTree.class)))))); - - private static final Matcher WITHIN_JUNIT_TEST = - enclosingClass(hasAnnotation("org.junit.runner.RunWith")); - - private static final Matcher ALLOWED_USAGES = - anyOf(WITHIN_GET_INSTANCE, WITHIN_GET_INSTANCE_IMPL, WITHIN_JUNIT_TEST); - - @Override - public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) { - if (ALLOWED_USAGES.matches(tree, state) || !CALL_TO_GET.matches(tree, state)) { - return Description.NO_MATCH; - } - return describeMatch(tree); - } - - private static Matcher isSupertypeOf(Supplier type) { - return new IsSupertypeOf(type); - } - - private static class IsSupertypeOf extends AbstractTypeMatcher { - - public IsSupertypeOf(Supplier typeToCompareSupplier) { - super(typeToCompareSupplier); - } - - public IsSupertypeOf(String typeString) { - super(typeString); - } - - @Override - public boolean matches(T tree, VisitorState state) { - return isSubtype(typeToCompareSupplier.get(state), getType(tree), state); - } - } -} diff --git a/tools/errorprone/src/test/java/com/google/firebase/FirebaseApp.java b/tools/errorprone/src/test/java/com/google/firebase/FirebaseApp.java deleted file mode 100644 index c1b2beafa88..00000000000 --- a/tools/errorprone/src/test/java/com/google/firebase/FirebaseApp.java +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2018 Google LLC -// -// 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.firebase; - -/** Fake FirebaseApp for testing purposes. */ -public class FirebaseApp { - public T get(Class anInterface) { - return null; - } -} diff --git a/tools/errorprone/src/test/java/com/google/firebase/errorprone/ComponentsAppGetCheckTest.java b/tools/errorprone/src/test/java/com/google/firebase/errorprone/ComponentsAppGetCheckTest.java deleted file mode 100644 index e50b758f1e3..00000000000 --- a/tools/errorprone/src/test/java/com/google/firebase/errorprone/ComponentsAppGetCheckTest.java +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2018 Google LLC -// -// 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.firebase.errorprone; - -import com.google.errorprone.CompilationTestHelper; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -@RunWith(JUnit4.class) -public class ComponentsAppGetCheckTest { - - private CompilationTestHelper compilationHelper; - - @Before - public void setup() { - compilationHelper = CompilationTestHelper.newInstance(ComponentsAppGetCheck.class, getClass()); - } - - @Test - public void testPositiveCases() { - compilationHelper.addSourceFile("FirebaseUseExplicitDependenciesPositiveCases.java").doTest(); - } - - @Test - public void testNegativeCases() { - compilationHelper.addSourceFile("FirebaseUseExplicitDependenciesNegativeCases.java").doTest(); - } -} diff --git a/tools/errorprone/src/test/resources/com/google/firebase/errorprone/FirebaseUseExplicitDependenciesNegativeCases.java b/tools/errorprone/src/test/resources/com/google/firebase/errorprone/FirebaseUseExplicitDependenciesNegativeCases.java deleted file mode 100644 index 009b61ab43d..00000000000 --- a/tools/errorprone/src/test/resources/com/google/firebase/errorprone/FirebaseUseExplicitDependenciesNegativeCases.java +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2018 Google LLC -// -// 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.firebase.errorprone; - -import com.google.firebase.FirebaseApp; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -public class FirebaseUseExplicitDependenciesNegativeCases { - - /** Valid use with one app parameter. */ - public static FirebaseUseExplicitDependenciesNegativeCases getInstance(FirebaseApp app) { - app.get(String.class); - return null; - } - - /** Valid use with multiple parameters. */ - public static FirebaseUseExplicitDependenciesNegativeCases getInstance( - FirebaseApp app, String foo) { - app.get(String.class); - return null; - } - - /** Valid use with no parameters. */ - public static FirebaseUseExplicitDependenciesNegativeCases getInstance() { - new FirebaseApp().get(String.class); - return null; - } - - /** Valid private use with multiple parameters. */ - private static FirebaseUseExplicitDependenciesNegativeCases getInstance( - FirebaseApp app, Integer i) { - app.get(String.class); - return null; - } - - /** Use allowed in tests. */ - @RunWith(JUnit4.class) - private static class MyTest { - public void test() { - new FirebaseApp().get(String.class); - } - } - - public static class SuperType {} - - public static class SubType extends SuperType { - public static SuperType getInstance(FirebaseApp app) { - app.get(String.class); - return null; - } - } - - public interface Iface {} - - public static class IfaceImpl implements Iface { - public static Iface getInstance(FirebaseApp app) { - app.get(String.class); - return null; - } - } -} diff --git a/tools/errorprone/src/test/resources/com/google/firebase/errorprone/FirebaseUseExplicitDependenciesPositiveCases.java b/tools/errorprone/src/test/resources/com/google/firebase/errorprone/FirebaseUseExplicitDependenciesPositiveCases.java deleted file mode 100644 index 800136f82d3..00000000000 --- a/tools/errorprone/src/test/resources/com/google/firebase/errorprone/FirebaseUseExplicitDependenciesPositiveCases.java +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2018 Google LLC -// -// 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.firebase.errorprone; - -import com.google.firebase.FirebaseApp; - -public class FirebaseUseExplicitDependenciesPositiveCases { - private FirebaseApp app = new FirebaseApp(); - - public void useOfAppGetInInstanceMethod(FirebaseApp app) { - // BUG: Diagnostic contains: FirebaseApp#get(Class) is discouraged - app.get(String.class); - } - - public void useOfAppGetInStaticMethod() { - // BUG: Diagnostic contains: FirebaseApp#get(Class) is discouraged - app.get(String.class); - } - - /** method returns void so it is not allowed. */ - public static void getInstance(FirebaseApp app, String foo) { - // BUG: Diagnostic contains: FirebaseApp#get(Class) is discouraged - app.get(String.class); - } - - /** method returns int so it is not allowed. */ - public static int getInstance(String foo) { - // BUG: Diagnostic contains: FirebaseApp#get(Class) is discouraged - new FirebaseApp().get(String.class); - return 0; - } - - /** method returns String so it is not allowed. */ - public static String getInstance(FirebaseApp app) { - // BUG: Diagnostic contains: FirebaseApp#get(Class) is discouraged - app.get(String.class); - return ""; - } -} diff --git a/tools/lint/src/main/kotlin/CheckRegistry.kt b/tools/lint/src/main/kotlin/CheckRegistry.kt index 4601a551abb..a3263522b16 100644 --- a/tools/lint/src/main/kotlin/CheckRegistry.kt +++ b/tools/lint/src/main/kotlin/CheckRegistry.kt @@ -33,6 +33,7 @@ class CheckRegistry : IssueRegistry() { ProviderAssignmentDetector.INVALID_PROVIDER_ASSIGNMENT, ThreadPoolDetector.THREAD_POOL_CREATION, TasksMainThreadDetector.TASK_MAIN_THREAD, + FirebaseAppGetDetector.ISSUE, ) override val api: Int diff --git a/tools/lint/src/main/kotlin/FirebaseAppGetDetector.kt b/tools/lint/src/main/kotlin/FirebaseAppGetDetector.kt new file mode 100644 index 00000000000..c6f20b0a235 --- /dev/null +++ b/tools/lint/src/main/kotlin/FirebaseAppGetDetector.kt @@ -0,0 +1,95 @@ +// Copyright 2022 Google LLC +// +// 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.firebase.lint.checks + +import com.android.tools.lint.detector.api.Category +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Implementation +import com.android.tools.lint.detector.api.Issue +import com.android.tools.lint.detector.api.JavaContext +import com.android.tools.lint.detector.api.Scope +import com.android.tools.lint.detector.api.Severity +import com.android.tools.lint.detector.api.SourceCodeScanner +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiMethod +import com.intellij.psi.util.InheritanceUtil +import org.jetbrains.uast.UCallExpression +import org.jetbrains.uast.UMethod +import org.jetbrains.uast.getParentOfType + +@Suppress("DetectorIsMissingAnnotations") +class FirebaseAppGetDetector : Detector(), SourceCodeScanner { + override fun getApplicableMethodNames(): List = listOf("get") + + override fun visitMethodCall(context: JavaContext, call: UCallExpression, method: PsiMethod) { + if (!isFirebaseAppGet(method)) { + return + } + + if (withinGetInstance(call)) { + return + } + call.getParentOfType() ?: return + context.report( + ISSUE, + call, + context.getCallLocation(call, includeReceiver = false, includeArguments = true), + "Use of FirebaseApp#get(Class) is discouraged, and is only acceptable" + + " in SDK#getInstance(...) methods. Instead declare dependencies explicitly in" + + " your ComponentRegistrar and inject." + ) + } + + private fun withinGetInstance(call: UCallExpression): Boolean { + val withinMethod = call.getParentOfType() ?: return false + if (withinMethod.name != "getInstance" && !withinMethod.isStatic) return false + + var containingClass: PsiClass = withinMethod.containingClass ?: return false + if (containingClass.name == "Companion") { + containingClass = containingClass.containingClass ?: return false + } + return InheritanceUtil.isInheritor( + withinMethod.returnType, + containingClass.qualifiedName ?: return false + ) + } + + private fun isFirebaseAppGet(method: PsiMethod): Boolean { + val cls = (method.parent as? PsiClass) ?: return false + return cls.qualifiedName == "com.google.firebase.FirebaseApp" && + method.parameterList.parametersCount == 1 + } + + companion object { + private val IMPLEMENTATION = + Implementation(FirebaseAppGetDetector::class.java, Scope.JAVA_FILE_SCOPE) + val ISSUE = + Issue.create( + "FirebaseUseExplicitDependencies", + briefDescription = + "Use of FirebaseApp#get(Class) is discouraged, and is only acceptable" + + " in SDK#getInstance(...) methods. Instead declare dependencies explicitly in" + + " your ComponentRegistrar and inject.", + explanation = + "Use of FirebaseApp#get(Class) is discouraged, and is only acceptable" + + " in SDK#getInstance(...) methods. Instead declare dependencies explicitly in" + + " your ComponentRegistrar and inject.", + category = Category.CORRECTNESS, + priority = 6, + severity = Severity.ERROR, + implementation = IMPLEMENTATION + ) + } +} diff --git a/tools/lint/src/test/kotlin/FirebaseAppGetDetectorTests.kt b/tools/lint/src/test/kotlin/FirebaseAppGetDetectorTests.kt new file mode 100644 index 00000000000..97c4b7e10fd --- /dev/null +++ b/tools/lint/src/test/kotlin/FirebaseAppGetDetectorTests.kt @@ -0,0 +1,180 @@ +// Copyright 2022 Google LLC +// +// 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.firebase.lint.checks + +import com.android.tools.lint.checks.infrastructure.LintDetectorTest +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Issue + +private val FIREBASE_APP = + """ + package com.google.firebase; + + public class FirebaseApp { + public T get(Class t) { + return null; + } + + public static FirebaseApp getInstance() { + return null; + } + } +""" + .trimIndent() + +class FirebaseAppGetDetectorTests : LintDetectorTest() { + override fun getDetector(): Detector = FirebaseAppGetDetector() + + override fun getIssues(): MutableList = mutableListOf(FirebaseAppGetDetector.ISSUE) + + fun test_app_get_from_getInstance_shouldNotFail() { + lint() + .files( + java(FIREBASE_APP), + java( + """ + import com.google.firebase.FirebaseApp; + + public class Foo { + public static Foo getInstance(FirebaseApp app) { + return app.get(Foo.class); + } + } + """ + .trimIndent() + ) + ) + .run() + .expectClean() + } + + fun test_app_get_from_getInstance_returningSubclass_shouldNotFail() { + lint() + .files( + java(FIREBASE_APP), + java( + """ + import com.google.firebase.FirebaseApp; + + public class Foo { + public static FooImpl getInstance(FirebaseApp app) { + return app.get(FooImpl.class); + } + } + class FooImpl extends Foo {} + """ + .trimIndent() + ) + ) + .run() + .expectClean() + } + + fun test_app_get_from_getInstance_withWrongReturnType_shouldFail() { + lint() + .files( + java(FIREBASE_APP), + java( + """ + import com.google.firebase.FirebaseApp; + + public class Foo { + public static String getInstance(FirebaseApp app) { + app.get(Foo.class); + return ""; + } + } + """ + .trimIndent() + ) + ) + .run() + .checkContains("Use of FirebaseApp#get(Class) is discouraged") + } + + fun test_app_get_from_getInstance_shouldNotFail_kotlin() { + lint() + .files( + java(FIREBASE_APP), + kotlin( + """ + import com.google.firebase.FirebaseApp + + class Foo { + companion object { + @JvmStatic + val instance = FirebaseApp.getInstance().get(Foo::class.java) + + @JvmStatic + fun getInstance(app: FirebaseApp) = app.get(Foo::class.java) + } + } + """ + .trimIndent() + ) + ) + .run() + .expectClean() + } + + fun test_app_get_from_getInstance_returningSubclass_shouldNotFail_kotlin() { + lint() + .files( + java(FIREBASE_APP), + kotlin( + """ + import com.google.firebase.FirebaseApp + + class Foo { + companion object { + @JvmStatic + val instance = FirebaseApp.getInstance().get(FooImpl::class.java) + + @JvmStatic + fun getInstance(app: FirebaseApp) = app.get(FooImpl::class.java) + } + } + class FooImpl : Foo() + """ + .trimIndent() + ) + ) + .run() + .expectClean() + } + + fun test_app_get_from_getInstance_withWrongReturnType_shouldFail_kotlin() { + lint() + .files( + java(FIREBASE_APP), + kotlin( + """ + import com.google.firebase.FirebaseApp; + + class Foo { + companion object { + + @JvmStatic + fun getInstance(app: FirebaseApp) = "".also { app.get(Foo::class.java) } + } + } + """ + .trimIndent() + ) + ) + .run() + .checkContains("Use of FirebaseApp#get(Class) is discouraged") + } +}