Android Lint: Fix constant evaluation in annotation parameter

#KT-17785 Fixed Target Versions 1.1.5
This commit is contained in:
Vyacheslav Gerasimov
2017-08-22 14:33:12 +03:00
parent 2e1edaf3d4
commit aedb4c0ade
6 changed files with 89 additions and 7 deletions

View File

@@ -16,9 +16,12 @@
package org.jetbrains.kotlin.android.lint
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl
import com.intellij.util.PathUtil
import org.jetbrains.android.inspections.klint.AndroidLintInspectionBase
import org.jetbrains.kotlin.android.KotlinAndroidTestCase
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.InTextDirectivesUtils.findStringWithPrefixes
import java.io.File
@@ -26,9 +29,9 @@ abstract class AbstractKotlinLintTest : KotlinAndroidTestCase() {
override fun setUp() {
super.setUp()
ConfigLibraryUtil.configureKotlinRuntime(myModule)
AndroidLintInspectionBase.invalidateInspectionShortName2IssueMap()
// needs access to .class files in kotlin runtime jar
(myFixture as CodeInsightTestFixtureImpl).setVirtualFileFilter { false } // Allow access to tree elements.
ConfigLibraryUtil.configureKotlinRuntime(myModule)
}
override fun tearDown() {
@@ -36,9 +39,11 @@ abstract class AbstractKotlinLintTest : KotlinAndroidTestCase() {
super.tearDown()
}
fun doTest(filename: String) {
val ktFile = File(filename)
val mainInspectionClassName = findStringWithPrefixes(ktFile.readText(), "// INSPECTION_CLASS: ") ?: error("Empty class name")
fun doTest(path: String) {
val ktFile = File(path)
val fileText = ktFile.readText()
val mainInspectionClassName = findStringWithPrefixes(fileText, "// INSPECTION_CLASS: ") ?: error("Empty class name")
val dependency = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// DEPENDENCY: ")
val inspectionClassNames = mutableListOf(mainInspectionClassName)
for (i in 2..100) {
@@ -63,9 +68,14 @@ abstract class AbstractKotlinLintTest : KotlinAndroidTestCase() {
}
}
val virtualFile = myFixture.copyFileToProject(ktFile.absolutePath, "src/" + getTestName(true) + ".kt")
val virtualFile = myFixture.copyFileToProject(ktFile.absolutePath, "src/${PathUtil.getFileName(path)}")
myFixture.configureFromExistingVirtualFile(virtualFile)
if (dependency != null) {
val (dependencyFile, dependencyTargetPath) = dependency.split(" -> ").map(String::trim)
myFixture.copyFileToProject("${PathUtil.getParentPath(path)}/$dependencyFile", "src/$dependencyTargetPath")
}
myFixture.checkHighlighting(true, false, false)
}
}

View File

@@ -138,6 +138,12 @@ public class KotlinLintTestGenerated extends AbstractKotlinLintTest {
doTest(fileName);
}
@TestMetadata("supportAnnotation.kt")
public void testSupportAnnotation() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/lint/supportAnnotation.kt");
doTest(fileName);
}
@TestMetadata("systemServices.kt")
public void testSystemServices() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/lint/systemServices.kt");

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2015 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 android.support.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.CLASS;
/**
* Denotes that the annotated element should be an int or long in the given range
* <p>
* Example:
* <pre><code>
* &#64;IntRange(from=0,to=255)
* public int getAlpha() {
* ...
* }
* </code></pre>
*/
@Retention(CLASS)
@Target({METHOD,PARAMETER,FIELD,LOCAL_VARIABLE,ANNOTATION_TYPE})
public @interface IntRange {
/** Smallest value, inclusive */
long from() default Long.MIN_VALUE;
/** Largest value, inclusive */
long to() default Long.MAX_VALUE;
}

View File

@@ -0,0 +1,16 @@
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSupportAnnotationUsageInspection
// DEPENDENCY: IntRange.java -> android/support/annotation/IntRange.java
import android.support.annotation.IntRange
import android.view.View
const val constantVal = 0L
<error descr="Invalid range: the `from` attribute must be less than the `to` attribute">@IntRange(from = 10, to = 0)</error>
fun invalidRange1a(): Int = 5
@IntRange(from = constantVal, to = 10) // ok
fun invalidRange0b(): Int = 5
<error descr="Invalid range: the `from` attribute must be less than the `to` attribute">@IntRange(from = 10, to = constantVal)</error>
fun invalidRange1b(): Int = 5

View File

@@ -105,6 +105,10 @@ any distributions of the compiler, libraries or plugin:
- Path: idea/testData/android/lintQuickfix/requiresApi/RequiresApi.java
- License: Apache 2 (license/third_party/aosp_license.txt)
- Origin: Copyright (C) 2011-15 The Android Open Source Project
- Path: idea/testData/android/lint/IntRange.java
- License: Apache 2 (license/third_party/aosp_license.txt)
- Origin: Copyright (C) 2011-15 The Android Open Source Project
- Path: libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/allOpenSpring/src/org/springframework/stereotype/Component.java
- License: Apache 2 (license/third_party/testdata/spring_license.txt)

View File

@@ -862,7 +862,7 @@ public class ConstantEvaluator {
}
}
return operandValue;
} else if (node instanceof UReferenceExpression) {
} else if (mContext != null && node instanceof UReferenceExpression) {
PsiElement resolved = ((UReferenceExpression) node).resolve();
if (resolved instanceof PsiVariable) {
PsiVariable variable = (PsiVariable) resolved;