Skip to content

Commit 33c3ba8

Browse files
committed
Fix Android SuppressLint and Api quickfixes: proper annotation placing
#KT-17783 Fixed #KT-17787 Fixed
1 parent e6001f5 commit 33c3ba8

File tree

8 files changed

+90
-20
lines changed

8 files changed

+90
-20
lines changed

idea/idea-android/src/org/jetbrains/kotlin/android/KtPsiUtil.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,18 @@
1616

1717
package org.jetbrains.kotlin.android
1818

19+
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
20+
import org.jetbrains.kotlin.idea.caches.resolve.analyze
1921
import org.jetbrains.kotlin.idea.editor.fixers.range
22+
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
2023
import org.jetbrains.kotlin.psi.KtClass
24+
import org.jetbrains.kotlin.psi.KtProperty
25+
import org.jetbrains.kotlin.resolve.BindingContext
26+
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
2127

2228
internal fun KtClass.insideBody(offset: Int): Boolean = getBody()?.range?.contains(offset) ?: false
29+
30+
fun KtProperty.hasBackingField(): Boolean {
31+
val propertyDescriptor = descriptor as? PropertyDescriptor ?: return false
32+
return analyze(BodyResolveMode.PARTIAL)[BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor] ?: false
33+
}

idea/idea-android/tests/org/jetbrains/kotlin/android/quickfix/AndroidLintQuickfixTestGenerated.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ public void testProperty() throws Exception {
113113
doTest(fileName);
114114
}
115115

116+
@TestMetadata("topLevelProperty.kt")
117+
public void testTopLevelProperty() throws Exception {
118+
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/lintQuickfix/requiresApi/topLevelProperty.kt");
119+
doTest(fileName);
120+
}
121+
116122
@TestMetadata("when.kt")
117123
public void testWhen() throws Exception {
118124
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/lintQuickfix/requiresApi/when.kt");
@@ -239,6 +245,12 @@ public void testProperty() throws Exception {
239245
doTest(fileName);
240246
}
241247

248+
@TestMetadata("topLevelProperty.kt")
249+
public void testTopLevelProperty() throws Exception {
250+
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/lintQuickfix/targetApi/topLevelProperty.kt");
251+
doTest(fileName);
252+
}
253+
242254
@TestMetadata("when.kt")
243255
public void testWhen() throws Exception {
244256
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/android/lintQuickfix/targetApi/when.kt");
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// INTENTION_TEXT: Add @RequiresApi(M) Annotation
2+
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection
3+
// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java
4+
import android.app.Activity
5+
6+
val top: Int
7+
get() = Activity().<caret>checkSelfPermission(READ_CONTACTS)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// INTENTION_TEXT: Add @RequiresApi(M) Annotation
2+
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection
3+
// DEPENDENCY: RequiresApi.java -> android/support/annotation/RequiresApi.java
4+
import android.app.Activity
5+
import android.os.Build
6+
import android.support.annotation.RequiresApi
7+
8+
val top: Int
9+
@RequiresApi(Build.VERSION_CODES.M)
10+
get() = Activity().checkSelfPermission(READ_CONTACTS)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// INTENTION_TEXT: Add @TargetApi(M) Annotation
2+
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection
3+
import android.app.Activity
4+
5+
val top: Int
6+
get() = Activity().<caret>checkSelfPermission(READ_CONTACTS)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// INTENTION_TEXT: Add @TargetApi(M) Annotation
2+
// INSPECTION_CLASS: org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection
3+
import android.annotation.TargetApi
4+
import android.app.Activity
5+
import android.os.Build
6+
7+
val top: Int
8+
@TargetApi(Build.VERSION_CODES.M)
9+
get() = Activity().checkSelfPermission(READ_CONTACTS)

plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AddTargetApiQuickFix.kt

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import com.intellij.codeInsight.FileModificationService
2222
import com.intellij.psi.PsiElement
2323
import com.intellij.psi.util.PsiTreeUtil
2424
import org.jetbrains.android.util.AndroidBundle
25+
import org.jetbrains.kotlin.android.hasBackingField
2526
import org.jetbrains.kotlin.idea.util.addAnnotation
2627
import org.jetbrains.kotlin.name.FqName
2728
import org.jetbrains.kotlin.psi.*
@@ -60,27 +61,33 @@ class AddTargetApiQuickFix(
6061
annotationContainer.addAnnotation(
6162
if (useRequiresApi) FQNAME_REQUIRES_API else FQNAME_TARGET_API,
6263
getAnnotationValue(true),
63-
whiteSpaceText = "\n")
64+
whiteSpaceText = if (annotationContainer.isNewLineNeededForAnnotation()) "\n" else " ")
6465
}
6566
}
6667

67-
private fun getAnnotationValue(fullyQualified: Boolean) = getVersionField(api, fullyQualified)
68+
private fun KtElement.isNewLineNeededForAnnotation() = !(this is KtParameter || this is KtTypeParameter || this is KtPropertyAccessor)
6869

69-
private fun getAnnotationContainer(element: PsiElement, useRequiresApi: Boolean) =
70-
PsiTreeUtil.findFirstParent(element) {
71-
if (useRequiresApi)
72-
it.isRequiresApiAnnotationValidTarget()
73-
else
74-
it.isTargetApiAnnotationValidTarget()
75-
}
70+
private fun getAnnotationValue(fullyQualified: Boolean) = getVersionField(api, fullyQualified)
7671

72+
private fun getAnnotationContainer(element: PsiElement, useRequiresApi: Boolean): PsiElement? {
73+
return PsiTreeUtil.findFirstParent(element) {
74+
if (useRequiresApi)
75+
it.isRequiresApiAnnotationValidTarget()
76+
else
77+
it.isTargetApiAnnotationValidTarget()
78+
}
79+
}
7780

78-
// TODO: KtFunctionLiteral is not supported now because addAnnotation fails to shorten references, investigate
79-
private fun PsiElement.isRequiresApiAnnotationValidTarget() = this is KtClassOrObject ||
80-
(this is KtFunction && this !is KtFunctionLiteral) ||
81-
(this is KtProperty && !this.isLocal)
81+
private fun PsiElement.isRequiresApiAnnotationValidTarget(): Boolean {
82+
return this is KtClassOrObject ||
83+
(this is KtFunction && this !is KtFunctionLiteral) ||
84+
(this is KtProperty && !isLocal && hasBackingField()) ||
85+
this is KtPropertyAccessor
86+
}
8287

83-
// TODO: KtFunctionLiteral is not supported now because addAnnotation fails to shorten references, investigate
84-
private fun PsiElement.isTargetApiAnnotationValidTarget() = this is KtClassOrObject ||
85-
(this is KtFunction && this !is KtFunctionLiteral)
88+
private fun PsiElement.isTargetApiAnnotationValidTarget(): Boolean {
89+
return this is KtClassOrObject ||
90+
(this is KtFunction && this !is KtFunctionLiteral) ||
91+
this is KtPropertyAccessor
92+
}
8693
}

plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/SuppressLintIntentionAction.kt

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import com.intellij.psi.PsiElement
2727
import com.intellij.psi.PsiFile
2828
import com.intellij.psi.util.PsiTreeUtil
2929
import org.jetbrains.android.util.AndroidBundle
30+
import org.jetbrains.kotlin.android.hasBackingField
3031
import org.jetbrains.kotlin.idea.util.addAnnotation
3132
import org.jetbrains.kotlin.name.FqName
3233
import org.jetbrains.kotlin.psi.*
@@ -97,9 +98,16 @@ class SuppressLintIntentionAction(val id: String, val element: PsiElement) : Int
9798
private fun getLintId(intentionId: String) =
9899
if (intentionId.startsWith(INTENTION_NAME_PREFIX)) intentionId.substring(INTENTION_NAME_PREFIX.length) else intentionId
99100

100-
private fun KtElement.isNewLineNeededForAnnotation() = !(this is KtParameter || this is KtTypeParameter)
101+
private fun KtElement.isNewLineNeededForAnnotation(): Boolean {
102+
return !(this is KtParameter ||
103+
this is KtTypeParameter ||
104+
this is KtPropertyAccessor)
105+
}
101106

102-
private fun PsiElement.isSuppressLintTarget() = this is KtDeclaration &&
103-
this !is KtDestructuringDeclaration &&
104-
this !is KtFunctionLiteral
107+
private fun PsiElement.isSuppressLintTarget(): Boolean {
108+
return this is KtDeclaration &&
109+
(this as? KtProperty)?.hasBackingField() ?: true &&
110+
this !is KtFunctionLiteral &&
111+
this !is KtDestructuringDeclaration
112+
}
105113
}

0 commit comments

Comments
 (0)