1 /*
2  * Copyright 2022 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 package androidx.build.lint
18 
19 import com.android.tools.lint.client.api.UElementHandler
20 import com.android.tools.lint.detector.api.Category
21 import com.android.tools.lint.detector.api.Detector
22 import com.android.tools.lint.detector.api.Implementation
23 import com.android.tools.lint.detector.api.Incident
24 import com.android.tools.lint.detector.api.Issue
25 import com.android.tools.lint.detector.api.JavaContext
26 import com.android.tools.lint.detector.api.Scope
27 import com.android.tools.lint.detector.api.Severity
28 import org.jetbrains.uast.UMethod
29 
30 @Suppress("UnstableApiUsage")
31 class BanInlineOptIn : Detector(), Detector.UastScanner {
32 
getApplicableUastTypesnull33     override fun getApplicableUastTypes() = listOf(UMethod::class.java)
34 
35     override fun createUastHandler(context: JavaContext): UElementHandler {
36         return MethodChecker(context)
37     }
38 
39     private inner class MethodChecker(val context: JavaContext) : UElementHandler() {
visitMethodnull40         override fun visitMethod(node: UMethod) {
41             val hasOptInAnnotation = context.evaluator.getAnnotation(node, "kotlin.OptIn") != null
42 
43             if (context.evaluator.isInline(node) && hasOptInAnnotation) {
44                 val incident =
45                     Incident(context, ISSUE)
46                         .location(context.getNameLocation((node)))
47                         .message("Inline functions cannot opt into experimental APIs.")
48                         .scope(node)
49                 context.report(incident)
50             }
51         }
52     }
53 
54     companion object {
55         val ISSUE =
56             Issue.create(
57                 id = "BanInlineOptIn",
58                 briefDescription = "Uses @OptIn annotation on an inline function",
59                 explanation =
60                     "Use of the @OptIn annotation is not allowed on inline functions," +
61                         " as libraries using this method will inline the reference to the opted-in" +
62                         " class. This can potentially create a compatibility issue.",
63                 category = Category.CORRECTNESS,
64                 priority = 5,
65                 severity = Severity.ERROR,
66                 implementation = Implementation(BanInlineOptIn::class.java, Scope.JAVA_FILE_SCOPE)
67             )
68     }
69 }
70