| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * 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.android.tools.lint.checks.studio |
| |
| import com.android.tools.lint.client.api.JavaEvaluator |
| 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.PsiElement |
| import com.intellij.psi.PsiMember |
| import com.intellij.psi.PsiMethod |
| import org.jetbrains.uast.UCallExpression |
| import org.jetbrains.uast.UExpression |
| import org.jetbrains.uast.UReferenceExpression |
| import org.jetbrains.uast.getContainingUMethod |
| |
| /** |
| * Detects Lint code that doesn't handle external annotations correctly. |
| */ |
| class ExternalAnnotationsDetector : Detector(), SourceCodeScanner { |
| |
| companion object Issues { |
| |
| @JvmField |
| val ISSUE = Issue.create( |
| id = "ExternalAnnotations", |
| briefDescription = "External annotations not considered", |
| explanation = """ |
| Lint supports XML files with "external annotations", which means any detectors that \ |
| recognize certain annotations should get them from `JavaEvaluator.getAllAnnotations` \ |
| and not by calling `uAnnotations` directly on UAST or PSI elements. |
| """, |
| severity = Severity.ERROR, |
| implementation = Implementation( |
| ExternalAnnotationsDetector::class.java, |
| Scope.JAVA_FILE_SCOPE |
| ) |
| ) |
| |
| private val relevantClasses = listOf( |
| "com.intellij.psi.PsiModifierListOwner", |
| "com.intellij.psi.PsiAnnotationOwner", |
| "org.jetbrains.uast.UAnnotated", |
| "com.intellij.lang.jvm.JvmAnnotatedElement" |
| ) |
| } |
| |
| override fun getApplicableMethodNames() = listOf("getAnnotations", "getUAnnotations") |
| override fun getApplicableReferenceNames() = listOf("annotations", "uAnnotations") |
| |
| override fun visitMethodCall( |
| context: JavaContext, |
| node: UCallExpression, |
| method: PsiMethod |
| ) { |
| check(node, method, context) |
| } |
| |
| override fun visitReference( |
| context: JavaContext, |
| reference: UReferenceExpression, |
| referenced: PsiElement |
| ) { |
| check(reference, referenced as? PsiMember ?: return, context) |
| } |
| |
| private fun check( |
| expression: UExpression, |
| member: PsiMember, |
| context: JavaContext |
| ) { |
| val evaluator = context.evaluator |
| if (relevantClasses.any { evaluator.isMemberInClass(member, it) } && |
| isRelevantCaller(expression, evaluator)) { |
| context.report( |
| ISSUE, |
| expression, |
| context.getLocation(expression), |
| "${member.name} used instead of `JavaContext.getAllAnnotations`." |
| ) |
| } |
| } |
| |
| private fun isRelevantCaller(node: UExpression, evaluator: JavaEvaluator): Boolean { |
| val callerClass = node.getContainingUMethod()?.containingClass ?: return false |
| return evaluator.inheritsFrom(callerClass, Detector::class.java.name, false) || |
| callerClass.qualifiedName.orEmpty().startsWith("com.android.tools.lint.") |
| } |
| } |