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 @file:Suppress("UnstableApiUsage")
18 
19 package androidx.navigation.common.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.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 com.android.tools.lint.detector.api.SourceCodeScanner
29 import com.intellij.psi.PsiMethod
30 import org.jetbrains.uast.UBlockExpression
31 import org.jetbrains.uast.UCallExpression
32 import org.jetbrains.uast.ULambdaExpression
33 
34 /**
35  * Lint for checking for empty construction of NavDeepLink in the Kotlin DSL, i.e. navDeepLink { }
36  */
37 class EmptyNavDeepLinkDetector : Detector(), SourceCodeScanner {
38     companion object {
39         val EmptyNavDeepLink =
40             Issue.create(
41                 id = "EmptyNavDeepLink",
42                 briefDescription =
43                     "NavDeepLink must define an uri, action, and/or mimetype to be " + "valid.",
44                 explanation =
45                     "Attempting to create an empty NavDeepLink will result in an " +
46                         "IllegalStateException at runtime. You may set these arguments within the lambda " +
47                         "of the call to navDeepLink.",
48                 category = Category.CORRECTNESS,
49                 severity = Severity.ERROR,
50                 implementation =
51                     Implementation(EmptyNavDeepLinkDetector::class.java, Scope.JAVA_FILE_SCOPE)
52             )
53     }
54 
getApplicableMethodNamesnull55     override fun getApplicableMethodNames(): List<String> = listOf("navDeepLink")
56 
57     override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
58         // valueArgumentCount should be 1 when navDeepLink is called
59         // safe args version of navDeepLink (reified T) can have no lambda, as the base uriPattern
60         // is passed in as param
61         if (node.valueArgumentCount > 0 && node.typeArgumentCount == 0) {
62             val lam = node.valueArguments[0] as ULambdaExpression
63             val body = lam.body as UBlockExpression
64             if (body.expressions.isEmpty()) {
65                 context.report(
66                     EmptyNavDeepLink,
67                     node,
68                     context.getNameLocation(node),
69                     "Creation of empty NavDeepLink"
70                 )
71             }
72         }
73     }
74 }
75