1 /* 2 * Copyright (C) 2018 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.build.lint 20 21 import com.android.tools.lint.detector.api.Category 22 import com.android.tools.lint.detector.api.Detector 23 import com.android.tools.lint.detector.api.Implementation 24 import com.android.tools.lint.detector.api.Incident 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.Scope 28 import com.android.tools.lint.detector.api.Severity 29 import com.intellij.psi.PsiMethod 30 import org.jetbrains.uast.UCallExpression 31 32 class ObsoleteBuildCompatUsageDetector : Detector(), Detector.UastScanner { 33 private val methodsToApiLevels = 34 mapOf( 35 "isAtLeastN" to 24, 36 "isAtLeastNMR1" to 25, 37 "isAtLeastO" to 26, 38 "isAtLeastOMR1" to 27, 39 "isAtLeastP" to 28, 40 "isAtLeastQ" to 29 41 ) 42 getApplicableMethodNamesnull43 override fun getApplicableMethodNames() = methodsToApiLevels.keys.toList() 44 45 override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) { 46 if (!context.evaluator.isMemberInClass(method, "androidx.core.os.BuildCompat")) { 47 return 48 } 49 50 // A receiver indicates the class name is part of the call (as opposed to static import). 51 val target = if (node.receiver != null) node.uastParent!! else node 52 53 val apiLevel = methodsToApiLevels[node.methodName] 54 val lintFix = 55 fix() 56 .name("Use SDK_INT >= $apiLevel") 57 .replace() 58 .text(target.asRenderString()) 59 .with("Build.VERSION.SDK_INT >= $apiLevel") 60 .build() 61 val incident = 62 Incident(context) 63 .fix(lintFix) 64 .issue(ISSUE) 65 .location(context.getLocation(node)) 66 .message("Using deprecated BuildCompat methods") 67 .scope(node) 68 context.report(incident) 69 } 70 71 companion object { 72 val ISSUE = 73 Issue.create( 74 "ObsoleteBuildCompat", 75 "Using deprecated BuildCompat methods", 76 "BuildConfig methods should only be used prior to an API level's finalization. " + 77 "Once an API level number is assigned, comparing directly with SDK_INT " + 78 "is preferred as it enables other lint checks to correctly work.", 79 Category.CORRECTNESS, 80 5, 81 Severity.ERROR, 82 Implementation(ObsoleteBuildCompatUsageDetector::class.java, Scope.JAVA_FILE_SCOPE) 83 ) 84 } 85 } 86