1 /* 2 * Copyright 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 @file:Suppress("UnstableApiUsage") 18 19 package androidx.work.lint 20 21 import com.android.tools.lint.detector.api.Category 22 import com.android.tools.lint.detector.api.Context 23 import com.android.tools.lint.detector.api.Detector 24 import com.android.tools.lint.detector.api.Implementation 25 import com.android.tools.lint.detector.api.Issue 26 import com.android.tools.lint.detector.api.JavaContext 27 import com.android.tools.lint.detector.api.Location 28 import com.android.tools.lint.detector.api.Scope 29 import com.android.tools.lint.detector.api.Severity 30 import com.android.tools.lint.detector.api.SourceCodeScanner 31 import org.jetbrains.uast.UClass 32 33 class BadConfigurationProviderIssueDetector : Detector(), SourceCodeScanner { 34 companion object { 35 val ISSUE = 36 Issue.create( 37 id = "BadConfigurationProvider", 38 briefDescription = "Invalid WorkManager Configuration Provider", 39 explanation = 40 """ 41 An `android.app.Application` must implement `androidx.work.Configuration.Provider` 42 for on-demand initialization. 43 """, 44 androidSpecific = true, 45 category = Category.CORRECTNESS, 46 severity = Severity.FATAL, 47 implementation = 48 Implementation( 49 BadConfigurationProviderIssueDetector::class.java, 50 Scope.JAVA_FILE_SCOPE 51 ) 52 ) 53 } 54 55 private var hasApplicableTypes = false 56 private var correct = false 57 private var location: Location? = null 58 applicableSuperClassesnull59 override fun applicableSuperClasses() = 60 listOf("android.app.Application", "androidx.work.Configuration.Provider") 61 62 override fun visitClass(context: JavaContext, declaration: UClass) { 63 if (correct) { 64 // Bail out early 65 return 66 } 67 68 val name = declaration.qualifiedName 69 if (name == "androidx.work.Configuration.Provider" || name == "android.app.Application") { 70 // Exempt base types from analysis 71 return 72 } 73 74 // Ignore abstract classes. 75 if (context.evaluator.isAbstract(declaration)) { 76 return 77 } 78 79 val isApplication = 80 context.evaluator.inheritsFrom(declaration.javaPsi, "android.app.Application", true) 81 82 val isProvider = 83 context.evaluator.inheritsFrom( 84 declaration.javaPsi, 85 "androidx.work.Configuration.Provider", 86 true 87 ) 88 89 if (isApplication) { 90 location = Location.create(context.file) 91 } 92 93 if (isProvider) { 94 hasApplicableTypes = true 95 } 96 97 if (isApplication && isProvider) { 98 correct = true 99 } 100 } 101 afterCheckRootProjectnull102 override fun afterCheckRootProject(context: Context) { 103 val location = location ?: return 104 if (hasApplicableTypes && !correct) { 105 context.report( 106 issue = ISSUE, 107 location = location, 108 message = "Expected Application subtype to implement Configuration.Provider" 109 ) 110 } 111 } 112 } 113