diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/KtPsiUtil.kt b/idea/idea-android/src/org/jetbrains/kotlin/android/KtPsiUtil.kt index 82a63d88cb8..cf00a6d4aac 100644 --- a/idea/idea-android/src/org/jetbrains/kotlin/android/KtPsiUtil.kt +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/KtPsiUtil.kt @@ -16,7 +16,18 @@ package org.jetbrains.kotlin.android +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.editor.fixers.range +import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor import org.jetbrains.kotlin.psi.KtClass +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode internal fun KtClass.insideBody(offset: Int): Boolean = getBody()?.range?.contains(offset) ?: false + +fun KtProperty.hasBackingField(): Boolean { + val propertyDescriptor = descriptor as? PropertyDescriptor ?: return false + return analyze(BodyResolveMode.PARTIAL)[BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor] ?: false +} \ No newline at end of file diff --git a/idea/idea-android/tests/org/jetbrains/kotlin/android/quickfix/AndroidLintQuickfixTestGenerated.java b/idea/idea-android/tests/org/jetbrains/kotlin/android/quickfix/AndroidLintQuickfixTestGenerated.java index 91dc9e6013c..059a35ef6f4 100644 --- a/idea/idea-android/tests/org/jetbrains/kotlin/android/quickfix/AndroidLintQuickfixTestGenerated.java +++ b/idea/idea-android/tests/org/jetbrains/kotlin/android/quickfix/AndroidLintQuickfixTestGenerated.java @@ -113,6 +113,12 @@ public class AndroidLintQuickfixTestGenerated extends AbstractAndroidLintQuickfi doTest(fileName); } + @TestMetadata("topLevelProperty.kt") + public void testTopLevelProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/lintQuickfix/requiresApi/topLevelProperty.kt"); + doTest(fileName); + } + @TestMetadata("when.kt") public void testWhen() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/lintQuickfix/requiresApi/when.kt"); @@ -239,6 +245,12 @@ public class AndroidLintQuickfixTestGenerated extends AbstractAndroidLintQuickfi doTest(fileName); } + @TestMetadata("topLevelProperty.kt") + public void testTopLevelProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/lintQuickfix/targetApi/topLevelProperty.kt"); + doTest(fileName); + } + @TestMetadata("when.kt") public void testWhen() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/lintQuickfix/targetApi/when.kt"); diff --git a/idea/testData/android/lintQuickfix/requiresApi/topLevelProperty.kt b/idea/testData/android/lintQuickfix/requiresApi/topLevelProperty.kt new file mode 100644 index 00000000000..d7f7f1f349a --- /dev/null +++ b/idea/testData/android/lintQuickfix/requiresApi/topLevelProperty.kt @@ -0,0 +1,7 @@ +// INTENTION_TEXT: Add @RequiresApi(M) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java +import android.app.Activity + +val top: Int + get() = Activity().checkSelfPermission(READ_CONTACTS) \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/requiresApi/topLevelProperty.kt.expected b/idea/testData/android/lintQuickfix/requiresApi/topLevelProperty.kt.expected new file mode 100644 index 00000000000..3460d7b564f --- /dev/null +++ b/idea/testData/android/lintQuickfix/requiresApi/topLevelProperty.kt.expected @@ -0,0 +1,10 @@ +// INTENTION_TEXT: Add @RequiresApi(M) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java +import android.app.Activity +import android.os.Build +import android.support.annotation.RequiresApi + +val top: Int + @RequiresApi(Build.VERSION_CODES.M) + get() = Activity().checkSelfPermission(READ_CONTACTS) \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetApi/topLevelProperty.kt b/idea/testData/android/lintQuickfix/targetApi/topLevelProperty.kt new file mode 100644 index 00000000000..1721eb0a819 --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetApi/topLevelProperty.kt @@ -0,0 +1,6 @@ +// INTENTION_TEXT: Add @TargetApi(M) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +import android.app.Activity + +val top: Int + get() = Activity().checkSelfPermission(READ_CONTACTS) \ No newline at end of file diff --git a/idea/testData/android/lintQuickfix/targetApi/topLevelProperty.kt.expected b/idea/testData/android/lintQuickfix/targetApi/topLevelProperty.kt.expected new file mode 100644 index 00000000000..869d8ab43ef --- /dev/null +++ b/idea/testData/android/lintQuickfix/targetApi/topLevelProperty.kt.expected @@ -0,0 +1,9 @@ +// INTENTION_TEXT: Add @TargetApi(M) Annotation +// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection +import android.annotation.TargetApi +import android.app.Activity +import android.os.Build + +val top: Int + @TargetApi(Build.VERSION_CODES.M) + get() = Activity().checkSelfPermission(READ_CONTACTS) \ No newline at end of file diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AddTargetApiQuickFix.kt b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AddTargetApiQuickFix.kt index 16618a463bb..5cf05ff6c70 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AddTargetApiQuickFix.kt +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AddTargetApiQuickFix.kt @@ -22,6 +22,7 @@ import com.intellij.codeInsight.FileModificationService import com.intellij.psi.PsiElement import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.android.util.AndroidBundle +import org.jetbrains.kotlin.android.hasBackingField import org.jetbrains.kotlin.idea.util.addAnnotation import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.* @@ -60,27 +61,33 @@ class AddTargetApiQuickFix( annotationContainer.addAnnotation( if (useRequiresApi) FQNAME_REQUIRES_API else FQNAME_TARGET_API, getAnnotationValue(true), - whiteSpaceText = "\n") + whiteSpaceText = if (annotationContainer.isNewLineNeededForAnnotation()) "\n" else " ") } } + private fun KtElement.isNewLineNeededForAnnotation() = !(this is KtParameter || this is KtTypeParameter || this is KtPropertyAccessor) + private fun getAnnotationValue(fullyQualified: Boolean) = getVersionField(api, fullyQualified) - private fun getAnnotationContainer(element: PsiElement, useRequiresApi: Boolean) = - PsiTreeUtil.findFirstParent(element) { - if (useRequiresApi) - it.isRequiresApiAnnotationValidTarget() - else - it.isTargetApiAnnotationValidTarget() - } + private fun getAnnotationContainer(element: PsiElement, useRequiresApi: Boolean): PsiElement? { + return PsiTreeUtil.findFirstParent(element) { + if (useRequiresApi) + it.isRequiresApiAnnotationValidTarget() + else + it.isTargetApiAnnotationValidTarget() + } + } + private fun PsiElement.isRequiresApiAnnotationValidTarget(): Boolean { + return this is KtClassOrObject || + (this is KtFunction && this !is KtFunctionLiteral) || + (this is KtProperty && !isLocal && hasBackingField()) || + this is KtPropertyAccessor + } - // TODO: KtFunctionLiteral is not supported now because addAnnotation fails to shorten references, investigate - private fun PsiElement.isRequiresApiAnnotationValidTarget() = this is KtClassOrObject || - (this is KtFunction && this !is KtFunctionLiteral) || - (this is KtProperty && !this.isLocal) - - // TODO: KtFunctionLiteral is not supported now because addAnnotation fails to shorten references, investigate - private fun PsiElement.isTargetApiAnnotationValidTarget() = this is KtClassOrObject || - (this is KtFunction && this !is KtFunctionLiteral) + private fun PsiElement.isTargetApiAnnotationValidTarget(): Boolean { + return this is KtClassOrObject || + (this is KtFunction && this !is KtFunctionLiteral) || + this is KtPropertyAccessor + } } \ No newline at end of file diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/SuppressLintIntentionAction.kt b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/SuppressLintIntentionAction.kt index e3ef7e95746..ec770839cfb 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/SuppressLintIntentionAction.kt +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/SuppressLintIntentionAction.kt @@ -27,6 +27,7 @@ import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.android.util.AndroidBundle +import org.jetbrains.kotlin.android.hasBackingField import org.jetbrains.kotlin.idea.util.addAnnotation import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.* @@ -97,9 +98,16 @@ class SuppressLintIntentionAction(val id: String, val element: PsiElement) : Int private fun getLintId(intentionId: String) = if (intentionId.startsWith(INTENTION_NAME_PREFIX)) intentionId.substring(INTENTION_NAME_PREFIX.length) else intentionId - private fun KtElement.isNewLineNeededForAnnotation() = !(this is KtParameter || this is KtTypeParameter) + private fun KtElement.isNewLineNeededForAnnotation(): Boolean { + return !(this is KtParameter || + this is KtTypeParameter || + this is KtPropertyAccessor) + } - private fun PsiElement.isSuppressLintTarget() = this is KtDeclaration && - this !is KtDestructuringDeclaration && - this !is KtFunctionLiteral + private fun PsiElement.isSuppressLintTarget(): Boolean { + return this is KtDeclaration && + (this as? KtProperty)?.hasBackingField() ?: true && + this !is KtFunctionLiteral && + this !is KtDestructuringDeclaration + } } \ No newline at end of file