KT-15092 Suppress inspection "use property access syntax" for some getters and fix completion for them
#KT-15092 fixed
This commit is contained in:
+7
-3
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
|||||||
import org.jetbrains.kotlin.idea.resolve.frontendService
|
import org.jetbrains.kotlin.idea.resolve.frontendService
|
||||||
import org.jetbrains.kotlin.idea.util.*
|
import org.jetbrains.kotlin.idea.util.*
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||||
import org.jetbrains.kotlin.psi.KtExpression
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
@@ -49,7 +50,8 @@ class ReferenceVariantsHelper(
|
|||||||
private val bindingContext: BindingContext,
|
private val bindingContext: BindingContext,
|
||||||
private val resolutionFacade: ResolutionFacade,
|
private val resolutionFacade: ResolutionFacade,
|
||||||
private val moduleDescriptor: ModuleDescriptor,
|
private val moduleDescriptor: ModuleDescriptor,
|
||||||
private val visibilityFilter: (DeclarationDescriptor) -> Boolean
|
private val visibilityFilter: (DeclarationDescriptor) -> Boolean,
|
||||||
|
private val notProperties: Set<FqNameUnsafe> = setOf()
|
||||||
) {
|
) {
|
||||||
fun getReferenceVariants(
|
fun getReferenceVariants(
|
||||||
expression: KtSimpleNameExpression,
|
expression: KtSimpleNameExpression,
|
||||||
@@ -96,7 +98,9 @@ class ReferenceVariantsHelper(
|
|||||||
|
|
||||||
fun filterOutJavaGettersAndSetters(variants: Collection<DeclarationDescriptor>): Collection<DeclarationDescriptor> {
|
fun filterOutJavaGettersAndSetters(variants: Collection<DeclarationDescriptor>): Collection<DeclarationDescriptor> {
|
||||||
val accessorMethodsToRemove = HashSet<FunctionDescriptor>()
|
val accessorMethodsToRemove = HashSet<FunctionDescriptor>()
|
||||||
for (variant in variants) {
|
val filteredVariants = variants.filter { it !is SyntheticJavaPropertyDescriptor || !it.suppressedByNotPropertyList(notProperties) }
|
||||||
|
|
||||||
|
for (variant in filteredVariants) {
|
||||||
if (variant is SyntheticJavaPropertyDescriptor) {
|
if (variant is SyntheticJavaPropertyDescriptor) {
|
||||||
accessorMethodsToRemove.add(variant.getMethod.original)
|
accessorMethodsToRemove.add(variant.getMethod.original)
|
||||||
|
|
||||||
@@ -107,7 +111,7 @@ class ReferenceVariantsHelper(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return variants.filter { it !is FunctionDescriptor || it.original !in accessorMethodsToRemove }
|
return filteredVariants.filter { it !is FunctionDescriptor || it.original !in accessorMethodsToRemove }
|
||||||
}
|
}
|
||||||
|
|
||||||
// filters out variable inside its initializer
|
// filters out variable inside its initializer
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* 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 org.jetbrains.kotlin.idea.util
|
||||||
|
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
|
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.overriddenTreeUniqueAsSequence
|
||||||
|
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
|
||||||
|
|
||||||
|
fun FunctionDescriptor.shouldNotConvertToProperty(notProperties: Set<FqNameUnsafe>): Boolean {
|
||||||
|
if (fqNameUnsafe in notProperties) return true
|
||||||
|
return this.overriddenTreeUniqueAsSequence(false).any { fqNameUnsafe in notProperties }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun SyntheticJavaPropertyDescriptor.suppressedByNotPropertyList(set: Set<FqNameUnsafe>) =
|
||||||
|
getMethod.shouldNotConvertToProperty(set) || setMethod?.shouldNotConvertToProperty(set) ?: false
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
<orderEntry type="library" scope="PROVIDED" name="java-i18n" level="project" />
|
<orderEntry type="library" scope="PROVIDED" name="java-i18n" level="project" />
|
||||||
<orderEntry type="library" scope="PROVIDED" name="gradle-and-groovy-plugin" level="project" />
|
<orderEntry type="library" scope="PROVIDED" name="gradle-and-groovy-plugin" level="project" />
|
||||||
<orderEntry type="module" module-name="eval4j" />
|
<orderEntry type="module" module-name="eval4j" />
|
||||||
<orderEntry type="library" name="intellij-core-analysis" level="project" />
|
<orderEntry type="library" exported="" name="intellij-core-analysis" level="project" />
|
||||||
<orderEntry type="module-library">
|
<orderEntry type="module-library">
|
||||||
<library>
|
<library>
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
|
|||||||
+4
-2
@@ -78,7 +78,9 @@ abstract class IntentionBasedInspection<TElement : PsiElement>(
|
|||||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
|
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
|
||||||
|
|
||||||
val intentionsAndCheckers = intentionInfos.map {
|
val intentionsAndCheckers = intentionInfos.map {
|
||||||
it.intention.constructors.single().call() to it.additionalChecker
|
val instance = it.intention.constructors.single().call()
|
||||||
|
instance.inspection = this
|
||||||
|
instance to it.additionalChecker
|
||||||
}
|
}
|
||||||
val elementType = intentionsAndCheckers.map { it.first.elementType }.distinct().singleOrNull()
|
val elementType = intentionsAndCheckers.map { it.first.elementType }.distinct().singleOrNull()
|
||||||
?: error("$intentionInfos should have the same elementType")
|
?: error("$intentionInfos should have the same elementType")
|
||||||
@@ -105,7 +107,7 @@ abstract class IntentionBasedInspection<TElement : PsiElement>(
|
|||||||
if (fixes == null) {
|
if (fixes == null) {
|
||||||
fixes = SmartList<LocalQuickFix>()
|
fixes = SmartList<LocalQuickFix>()
|
||||||
}
|
}
|
||||||
fixes!!.add(createQuickFix(intention, additionalChecker, targetElement))
|
fixes.add(createQuickFix(intention, additionalChecker, targetElement))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -91,8 +91,11 @@ abstract class SelfTargetingIntention<TElement : PsiElement>(
|
|||||||
return !isIntentionBaseInspectionEnabled(project, target)
|
return !isIntentionBaseInspectionEnabled(project, target)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var inspection: IntentionBasedInspection<TElement>? = null
|
||||||
|
internal set
|
||||||
|
|
||||||
protected fun isIntentionBaseInspectionEnabled(project: Project, target: TElement): Boolean {
|
protected fun isIntentionBaseInspectionEnabled(project: Project, target: TElement): Boolean {
|
||||||
val inspection = findInspection(this.javaClass.kotlin) ?: return false
|
val inspection = inspection ?: findInspection(this.javaClass.kotlin) ?: return false
|
||||||
|
|
||||||
val key = HighlightDisplayKey.find(inspection.shortName)
|
val key = HighlightDisplayKey.find(inspection.shortName)
|
||||||
if (!InspectionProjectProfileManager.getInstance(project).inspectionProfile.isToolEnabled(key, target)) {
|
if (!InspectionProjectProfileManager.getInstance(project).inspectionProfile.isToolEnabled(key, target)) {
|
||||||
|
|||||||
@@ -123,7 +123,11 @@ abstract class CompletionSession(
|
|||||||
protected val isVisibleFilter: (DeclarationDescriptor) -> Boolean = { isVisibleDescriptor(it, completeNonAccessible = configuration.nonAccessibleDeclarations) }
|
protected val isVisibleFilter: (DeclarationDescriptor) -> Boolean = { isVisibleDescriptor(it, completeNonAccessible = configuration.nonAccessibleDeclarations) }
|
||||||
protected val isVisibleFilterCheckAlways: (DeclarationDescriptor) -> Boolean = { isVisibleDescriptor(it, completeNonAccessible = false) }
|
protected val isVisibleFilterCheckAlways: (DeclarationDescriptor) -> Boolean = { isVisibleDescriptor(it, completeNonAccessible = false) }
|
||||||
|
|
||||||
protected val referenceVariantsHelper = ReferenceVariantsHelper(bindingContext, resolutionFacade, moduleDescriptor, isVisibleFilter)
|
protected val referenceVariantsHelper = ReferenceVariantsHelper(bindingContext,
|
||||||
|
resolutionFacade,
|
||||||
|
moduleDescriptor,
|
||||||
|
isVisibleFilter,
|
||||||
|
NotPropertiesService.getNotProperties(position))
|
||||||
|
|
||||||
protected val callTypeAndReceiver: CallTypeAndReceiver<*, *>
|
protected val callTypeAndReceiver: CallTypeAndReceiver<*, *>
|
||||||
protected val receiverTypes: Collection<ReceiverType>?
|
protected val receiverTypes: Collection<ReceiverType>?
|
||||||
|
|||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
import java.net.Socket
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
val s = Socket()
|
||||||
|
s.<caret>
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXIST: {"lookupString":"getInputStream","tailText":"()","typeText":"InputStream!","attributes":"bold","allLookupStrings":"getInputStream","itemText":"getInputStream"}
|
||||||
|
// ABSENT: inputStream
|
||||||
+6
@@ -2917,6 +2917,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("SuppressedByNotPropertyList.kt")
|
||||||
|
public void testSuppressedByNotPropertyList() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/java/syntheticExtensions/SuppressedByNotPropertyList.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("SyntheticExtensions1.kt")
|
@TestMetadata("SyntheticExtensions1.kt")
|
||||||
public void testSyntheticExtensions1() throws Exception {
|
public void testSyntheticExtensions1() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/java/syntheticExtensions/SyntheticExtensions1.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/java/syntheticExtensions/SyntheticExtensions1.kt");
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* 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 org.jetbrains.kotlin.idea.core
|
||||||
|
|
||||||
|
import com.intellij.openapi.components.ServiceManager
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||||
|
|
||||||
|
interface NotPropertiesService {
|
||||||
|
|
||||||
|
fun getNotProperties(element: PsiElement): Set<FqNameUnsafe>
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun getInstance(project: Project): NotPropertiesService {
|
||||||
|
return ServiceManager.getService(project, NotPropertiesService::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getNotProperties(element: PsiElement) =
|
||||||
|
getInstance(element.project).getNotProperties(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-1
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
|||||||
import org.jetbrains.kotlin.idea.codeInsight.ReferenceVariantsHelper
|
import org.jetbrains.kotlin.idea.codeInsight.ReferenceVariantsHelper
|
||||||
import org.jetbrains.kotlin.idea.completion.BasicLookupElementFactory
|
import org.jetbrains.kotlin.idea.completion.BasicLookupElementFactory
|
||||||
import org.jetbrains.kotlin.idea.completion.InsertHandlerProvider
|
import org.jetbrains.kotlin.idea.completion.InsertHandlerProvider
|
||||||
|
import org.jetbrains.kotlin.idea.core.NotPropertiesService
|
||||||
import org.jetbrains.kotlin.idea.core.isVisible
|
import org.jetbrains.kotlin.idea.core.isVisible
|
||||||
import org.jetbrains.kotlin.idea.util.CallType
|
import org.jetbrains.kotlin.idea.util.CallType
|
||||||
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
|
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
|
||||||
@@ -60,7 +61,7 @@ abstract class BaseKotlinVariableMacro<TState> : Macro() {
|
|||||||
|
|
||||||
val state = initState(contextElement, bindingContext)
|
val state = initState(contextElement, bindingContext)
|
||||||
|
|
||||||
val helper = ReferenceVariantsHelper(bindingContext, resolutionFacade, resolutionFacade.moduleDescriptor, ::isVisible)
|
val helper = ReferenceVariantsHelper(bindingContext, resolutionFacade, resolutionFacade.moduleDescriptor, ::isVisible, NotPropertiesService.getNotProperties(contextElement))
|
||||||
return helper
|
return helper
|
||||||
.getReferenceVariants(contextElement, CallTypeAndReceiver.DEFAULT, DescriptorKindFilter.VARIABLES, { true })
|
.getReferenceVariants(contextElement, CallTypeAndReceiver.DEFAULT, DescriptorKindFilter.VARIABLES, { true })
|
||||||
.map { it as VariableDescriptor }
|
.map { it as VariableDescriptor }
|
||||||
|
|||||||
@@ -119,6 +119,11 @@
|
|||||||
<add-to-group group-id="KotlinToolsGroup" anchor="last"/>
|
<add-to-group group-id="KotlinToolsGroup" anchor="last"/>
|
||||||
</action>
|
</action>
|
||||||
|
|
||||||
|
<action id="PrintOutNotPropertyMatches" class="org.jetbrains.kotlin.idea.actions.internal.SearchNotPropertyCandidatesAction"
|
||||||
|
text="Search Not Property Candidates">
|
||||||
|
<add-to-group group-id="KotlinToolsGroup" anchor="last"/>
|
||||||
|
</action>
|
||||||
|
|
||||||
<action id="FindImplicitNothingAction" class="org.jetbrains.kotlin.idea.actions.internal.FindImplicitNothingAction"
|
<action id="FindImplicitNothingAction" class="org.jetbrains.kotlin.idea.actions.internal.FindImplicitNothingAction"
|
||||||
text="Find Implicit Nothing Calls">
|
text="Find Implicit Nothing Calls">
|
||||||
<add-to-group group-id="KotlinToolsGroup" anchor="last"/>
|
<add-to-group group-id="KotlinToolsGroup" anchor="last"/>
|
||||||
@@ -306,6 +311,9 @@
|
|||||||
<projectService serviceInterface="org.jetbrains.kotlin.config.KotlinFacetSettingsProvider"
|
<projectService serviceInterface="org.jetbrains.kotlin.config.KotlinFacetSettingsProvider"
|
||||||
serviceImplementation="org.jetbrains.kotlin.idea.facet.KotlinFacetSettingsProviderImpl"/>
|
serviceImplementation="org.jetbrains.kotlin.idea.facet.KotlinFacetSettingsProviderImpl"/>
|
||||||
|
|
||||||
|
<projectService serviceInterface="org.jetbrains.kotlin.idea.core.NotPropertiesService"
|
||||||
|
serviceImplementation="org.jetbrains.kotlin.idea.intentions.NotPropertiesServiceImpl"/>
|
||||||
|
|
||||||
<errorHandler implementation="org.jetbrains.kotlin.idea.reporter.KotlinReportSubmitter"/>
|
<errorHandler implementation="org.jetbrains.kotlin.idea.reporter.KotlinReportSubmitter"/>
|
||||||
|
|
||||||
<internalFileTemplate name="Kotlin File"/>
|
<internalFileTemplate name="Kotlin File"/>
|
||||||
@@ -1612,6 +1620,7 @@
|
|||||||
|
|
||||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.UsePropertyAccessSyntaxInspection"
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.UsePropertyAccessSyntaxInspection"
|
||||||
displayName="Accessor call that can be replaced with property access syntax"
|
displayName="Accessor call that can be replaced with property access syntax"
|
||||||
|
shortName="UsePropertyAccessSyntax"
|
||||||
groupName="Kotlin"
|
groupName="Kotlin"
|
||||||
cleanupTool="true"
|
cleanupTool="true"
|
||||||
enabledByDefault="true"
|
enabledByDefault="true"
|
||||||
|
|||||||
+167
@@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* 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 org.jetbrains.kotlin.idea.actions.internal
|
||||||
|
|
||||||
|
|
||||||
|
import com.intellij.openapi.actionSystem.AnAction
|
||||||
|
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||||
|
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||||
|
import com.intellij.openapi.progress.ProgressManager
|
||||||
|
import com.intellij.openapi.progress.ProgressManager.progress
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.ui.Messages
|
||||||
|
import com.intellij.psi.PsiMethod
|
||||||
|
import com.intellij.psi.PsiModifier
|
||||||
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
|
import org.jetbrains.kotlin.load.java.isFromJava
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||||
|
|
||||||
|
|
||||||
|
class SearchNotPropertyCandidatesAction : AnAction() {
|
||||||
|
override fun actionPerformed(e: AnActionEvent?) {
|
||||||
|
val project = e?.project!!
|
||||||
|
val psiFile = e.getData(CommonDataKeys.PSI_FILE) as? KtFile ?: return
|
||||||
|
|
||||||
|
val desc = psiFile.findModuleDescriptor()
|
||||||
|
val result = Messages.showInputDialog("Enter package FqName", "Search for Not Property candidates", Messages.getQuestionIcon())
|
||||||
|
val packageDesc = try {
|
||||||
|
val fqName = FqName.fromSegments(result!!.split('.'))
|
||||||
|
desc.getPackage(fqName)
|
||||||
|
}
|
||||||
|
catch (e: Exception) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
||||||
|
{
|
||||||
|
runReadAction {
|
||||||
|
ProgressManager.getInstance().progressIndicator.isIndeterminate = true
|
||||||
|
processAllDescriptors(packageDesc, project)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Searching for Not Property candidates",
|
||||||
|
true,
|
||||||
|
project)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun processAllDescriptors(desc: DeclarationDescriptor, project: Project) {
|
||||||
|
val processed = mutableSetOf<DeclarationDescriptor>()
|
||||||
|
var pFunctions = 0
|
||||||
|
val matchedDescriptors = mutableSetOf<FunctionDescriptor>()
|
||||||
|
|
||||||
|
fun recursive(desc: DeclarationDescriptor) {
|
||||||
|
if (desc in processed) return
|
||||||
|
progress("Step 1: Collecting ${processed.size}:$pFunctions:${matchedDescriptors.size}", "$desc")
|
||||||
|
when (desc) {
|
||||||
|
|
||||||
|
is ModuleDescriptor -> recursive(desc.getPackage(FqName("java")))
|
||||||
|
is ClassDescriptor -> desc.unsubstitutedMemberScope.getContributedDescriptors { true }.forEach(::recursive)
|
||||||
|
is PackageViewDescriptor -> desc.memberScope.getContributedDescriptors { true }.forEach(::recursive)
|
||||||
|
|
||||||
|
is FunctionDescriptor -> {
|
||||||
|
if (desc.isFromJava) {
|
||||||
|
val name = desc.fqNameUnsafe.shortName().asString()
|
||||||
|
if (name.length > 3 &&
|
||||||
|
((name.startsWith("get") && desc.valueParameters.isEmpty() && desc.returnType != null) ||
|
||||||
|
(name.startsWith("set") && desc.valueParameters.size == 1))) {
|
||||||
|
if (desc in matchedDescriptors) return
|
||||||
|
matchedDescriptors += desc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pFunctions++
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
processed += desc
|
||||||
|
}
|
||||||
|
recursive(desc)
|
||||||
|
val resultDescriptors = mutableSetOf<FunctionDescriptor>()
|
||||||
|
matchedDescriptors.flatMapTo(resultDescriptors) {
|
||||||
|
sequenceOf(it, *(it.overriddenDescriptors.toTypedArray())).asIterable()
|
||||||
|
}
|
||||||
|
println("Found ${resultDescriptors.size} accessors")
|
||||||
|
|
||||||
|
|
||||||
|
fun PsiMethod.isTrivial(): Boolean {
|
||||||
|
val t = this.text
|
||||||
|
val s = t.indexOf('{')
|
||||||
|
val e = t.lastIndexOf('}')
|
||||||
|
if (s != e && s != -1) {
|
||||||
|
return t.substring(s, e).lines().size <= 3
|
||||||
|
}
|
||||||
|
else return true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val descriptorToPsiBinding = mutableMapOf<FunctionDescriptor, PsiMethod>()
|
||||||
|
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
resultDescriptors.forEach { desc ->
|
||||||
|
progress("Step 2: ${i++} of ${resultDescriptors.size}", "$desc")
|
||||||
|
val source = DescriptorToSourceUtilsIde.getAllDeclarations(project, desc)
|
||||||
|
.filterIsInstance<PsiMethod>()
|
||||||
|
.firstOrNull() ?: return@forEach
|
||||||
|
val abstract = source.modifierList.hasModifierProperty(PsiModifier.ABSTRACT)
|
||||||
|
if (!abstract) {
|
||||||
|
if (!source.isTrivial()) {
|
||||||
|
descriptorToPsiBinding[desc] = source
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val nonTrivial = mutableSetOf<FunctionDescriptor>()
|
||||||
|
i = 0
|
||||||
|
descriptorToPsiBinding.forEach { t, u ->
|
||||||
|
progress("Step 3: ${i++} of ${descriptorToPsiBinding.size}", "$t")
|
||||||
|
val descriptors = t.overriddenDescriptors
|
||||||
|
var impl = false
|
||||||
|
descriptors.forEach {
|
||||||
|
val source = DescriptorToSourceUtilsIde.getAllDeclarations(project, it).filterIsInstance<PsiMethod>().firstOrNull()
|
||||||
|
if (source != null) {
|
||||||
|
if (source.body != null || source.hasModifierProperty(PsiModifier.ABSTRACT))
|
||||||
|
nonTrivial += it
|
||||||
|
impl = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (u.body != null)
|
||||||
|
if (!impl)
|
||||||
|
nonTrivial += t
|
||||||
|
}
|
||||||
|
nonTrivial.forEach(::println)
|
||||||
|
println("Non trivial count: ${nonTrivial.size}")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun update(e: AnActionEvent) {
|
||||||
|
if (!KotlinInternalMode.enabled) {
|
||||||
|
e.presentation.isVisible = false
|
||||||
|
e.presentation.isEnabled = false
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
e.presentation.isVisible = true
|
||||||
|
e.presentation.isEnabled = e.getData(CommonDataKeys.PSI_FILE) is KtFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* 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 org.jetbrains.kotlin.idea.configuration.ui
|
||||||
|
|
||||||
|
import com.intellij.openapi.ui.Messages
|
||||||
|
import com.intellij.openapi.ui.NonEmptyInputValidator
|
||||||
|
import com.intellij.ui.AddEditRemovePanel
|
||||||
|
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||||
|
|
||||||
|
class NotPropertyListPanel(data: List<FqNameUnsafe>) : AddEditRemovePanel<FqNameUnsafe>(MyTableModel(), data) {
|
||||||
|
|
||||||
|
var modified = false
|
||||||
|
|
||||||
|
override fun removeItem(fqName: FqNameUnsafe): Boolean {
|
||||||
|
modified = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun editItem(fqName: FqNameUnsafe): FqNameUnsafe? {
|
||||||
|
val result = Messages.showInputDialog(this, "Enter fully-qualified method name:",
|
||||||
|
"Edit exclusion",
|
||||||
|
Messages.getQuestionIcon(),
|
||||||
|
fqName.asString(),
|
||||||
|
NonEmptyInputValidator()
|
||||||
|
) ?: return null
|
||||||
|
|
||||||
|
val created = FqNameUnsafe(result)
|
||||||
|
|
||||||
|
if (created in data)
|
||||||
|
return null
|
||||||
|
|
||||||
|
modified = true
|
||||||
|
return created
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addItem(): FqNameUnsafe? {
|
||||||
|
val result = Messages.showInputDialog(this, "Enter fully-qualified method name:",
|
||||||
|
"Add exclusion",
|
||||||
|
Messages.getQuestionIcon(),
|
||||||
|
"",
|
||||||
|
NonEmptyInputValidator()
|
||||||
|
) ?: return null
|
||||||
|
|
||||||
|
val created = FqNameUnsafe(result)
|
||||||
|
|
||||||
|
if (created in data)
|
||||||
|
return null
|
||||||
|
|
||||||
|
modified = true
|
||||||
|
return created
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyTableModel : AddEditRemovePanel.TableModel<FqNameUnsafe>() {
|
||||||
|
override fun getField(o: FqNameUnsafe, columnIndex: Int) = o.asString()
|
||||||
|
override fun getColumnName(columnIndex: Int) = "Method"
|
||||||
|
override fun getColumnCount() = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -18,17 +18,26 @@ package org.jetbrains.kotlin.idea.intentions
|
|||||||
|
|
||||||
import com.intellij.codeInspection.CleanupLocalInspectionTool
|
import com.intellij.codeInspection.CleanupLocalInspectionTool
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.ui.LabeledComponent
|
||||||
|
import com.intellij.openapi.util.Key
|
||||||
|
import com.intellij.profile.codeInspection.InspectionProjectProfileManager
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.diagnostics.Severity
|
import org.jetbrains.kotlin.diagnostics.Severity
|
||||||
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
|
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||||
|
import org.jetbrains.kotlin.idea.configuration.ui.NotPropertyListPanel
|
||||||
|
import org.jetbrains.kotlin.idea.core.NotPropertiesService
|
||||||
import org.jetbrains.kotlin.idea.core.copied
|
import org.jetbrains.kotlin.idea.core.copied
|
||||||
import org.jetbrains.kotlin.idea.core.replaced
|
import org.jetbrains.kotlin.idea.core.replaced
|
||||||
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
|
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
|
||||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||||
import org.jetbrains.kotlin.idea.resolve.frontendService
|
import org.jetbrains.kotlin.idea.resolve.frontendService
|
||||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||||
|
import org.jetbrains.kotlin.idea.util.shouldNotConvertToProperty
|
||||||
|
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector
|
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector
|
||||||
@@ -36,7 +45,6 @@ import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis
|
|||||||
import org.jetbrains.kotlin.renderer.render
|
import org.jetbrains.kotlin.renderer.render
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
|
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
|
||||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoAfter
|
|
||||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoBefore
|
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoBefore
|
||||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
|
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
|
||||||
import org.jetbrains.kotlin.resolve.calls.CallResolver
|
import org.jetbrains.kotlin.resolve.calls.CallResolver
|
||||||
@@ -55,8 +63,54 @@ import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
|
|||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||||
|
import javax.swing.JComponent
|
||||||
|
|
||||||
class UsePropertyAccessSyntaxInspection : IntentionBasedInspection<KtCallExpression>(UsePropertyAccessSyntaxIntention::class), CleanupLocalInspectionTool
|
class UsePropertyAccessSyntaxInspection : IntentionBasedInspection<KtCallExpression>(UsePropertyAccessSyntaxIntention::class), CleanupLocalInspectionTool {
|
||||||
|
|
||||||
|
val fqNameList = mutableListOf<FqNameUnsafe>()
|
||||||
|
|
||||||
|
var fqNameStrings: List<String>
|
||||||
|
get() = fqNameList.map { it.asString() }
|
||||||
|
set(value) {
|
||||||
|
fqNameList.clear()
|
||||||
|
value.mapTo(fqNameList, ::FqNameUnsafe)
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
fqNameStrings = NotPropertiesServiceImpl.default
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createOptionsPanel(): JComponent? {
|
||||||
|
val list = NotPropertyListPanel(fqNameList)
|
||||||
|
return LabeledComponent.create(list, "Excluded methods")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class NotPropertiesServiceImpl(private val project: Project) : NotPropertiesService {
|
||||||
|
override fun getNotProperties(element: PsiElement): Set<FqNameUnsafe> {
|
||||||
|
val profile = InspectionProjectProfileManager.getInstance(project).inspectionProfile
|
||||||
|
val tool = profile.getUnwrappedTool(USE_PROPERTY_ACCESS_INSPECTION, element)
|
||||||
|
return (tool?.fqNameList ?: default.map(::FqNameUnsafe)).toSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
val default = listOf(
|
||||||
|
"java.net.Socket.getInputStream",
|
||||||
|
"java.net.Socket.getOutputStream",
|
||||||
|
"java.net.URLConnection.getInputStream",
|
||||||
|
"java.net.URLConnection.getOutputStream",
|
||||||
|
"java.util.concurrent.atomic.AtomicInteger.getAndIncrement",
|
||||||
|
"java.util.concurrent.atomic.AtomicInteger.getAndDecrement",
|
||||||
|
"java.util.concurrent.atomic.AtomicLong.getAndIncrement",
|
||||||
|
"java.util.concurrent.atomic.AtomicLong.getAndDecrement"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
val USE_PROPERTY_ACCESS_INSPECTION: Key<UsePropertyAccessSyntaxInspection> = Key.create("UsePropertyAccessSyntax")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class UsePropertyAccessSyntaxIntention : SelfTargetingOffsetIndependentIntention<KtCallExpression>(KtCallExpression::class.java, "Use property access syntax") {
|
class UsePropertyAccessSyntaxIntention : SelfTargetingOffsetIndependentIntention<KtCallExpression>(KtCallExpression::class.java, "Use property access syntax") {
|
||||||
override fun isApplicableTo(element: KtCallExpression): Boolean {
|
override fun isApplicableTo(element: KtCallExpression): Boolean {
|
||||||
@@ -87,6 +141,11 @@ class UsePropertyAccessSyntaxIntention : SelfTargetingOffsetIndependentIntention
|
|||||||
if (!resolvedCall.isReallySuccess()) return null
|
if (!resolvedCall.isReallySuccess()) return null
|
||||||
|
|
||||||
val function = resolvedCall.resultingDescriptor as? FunctionDescriptor ?: return null
|
val function = resolvedCall.resultingDescriptor as? FunctionDescriptor ?: return null
|
||||||
|
|
||||||
|
val notProperties = (inspection as? UsePropertyAccessSyntaxInspection)?.fqNameList?.toSet() ?:
|
||||||
|
NotPropertiesService.getNotProperties(callExpression)
|
||||||
|
if (function.shouldNotConvertToProperty(notProperties)) return null
|
||||||
|
|
||||||
val resolutionScope = callExpression.getResolutionScope(bindingContext, resolutionFacade)
|
val resolutionScope = callExpression.getResolutionScope(bindingContext, resolutionFacade)
|
||||||
val property = findSyntheticProperty(function, resolutionFacade.getFrontendService(SyntheticScopes::class.java)) ?: return null
|
val property = findSyntheticProperty(function, resolutionFacade.getFrontendService(SyntheticScopes::class.java)) ?: return null
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
|
|||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||||
import org.jetbrains.kotlin.idea.codeInsight.ReferenceVariantsHelper
|
import org.jetbrains.kotlin.idea.codeInsight.ReferenceVariantsHelper
|
||||||
|
import org.jetbrains.kotlin.idea.core.NotPropertiesService
|
||||||
import org.jetbrains.kotlin.idea.core.isVisible
|
import org.jetbrains.kotlin.idea.core.isVisible
|
||||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.guessTypes
|
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.guessTypes
|
||||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.KotlinPsiUnifier
|
import org.jetbrains.kotlin.idea.util.psi.patternMatching.KotlinPsiUnifier
|
||||||
@@ -101,9 +102,9 @@ class RenameUnresolvedReferenceFix(element: KtNameReferenceExpression): KotlinQu
|
|||||||
val resolutionFacade = element.getResolutionFacade()
|
val resolutionFacade = element.getResolutionFacade()
|
||||||
val context = resolutionFacade.analyze(element, BodyResolveMode.PARTIAL)
|
val context = resolutionFacade.analyze(element, BodyResolveMode.PARTIAL)
|
||||||
val moduleDescriptor = resolutionFacade.moduleDescriptor
|
val moduleDescriptor = resolutionFacade.moduleDescriptor
|
||||||
val variantsHelper = ReferenceVariantsHelper(context, resolutionFacade, moduleDescriptor) {
|
val variantsHelper = ReferenceVariantsHelper(context, resolutionFacade, moduleDescriptor, {
|
||||||
it !is DeclarationDescriptorWithVisibility || it.isVisible(element, null, context, resolutionFacade)
|
it !is DeclarationDescriptorWithVisibility || it.isVisible(element, null, context, resolutionFacade)
|
||||||
}
|
}, NotPropertiesService.getNotProperties(element))
|
||||||
val expectedTypes = patternExpression
|
val expectedTypes = patternExpression
|
||||||
.guessTypes(context, moduleDescriptor)
|
.guessTypes(context, moduleDescriptor)
|
||||||
.ifEmpty { arrayOf(moduleDescriptor.builtIns.nullableAnyType) }
|
.ifEmpty { arrayOf(moduleDescriptor.builtIns.nullableAnyType) }
|
||||||
|
|||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_JDK
|
||||||
|
// IS_APPLICABLE: false
|
||||||
|
import java.net.Socket
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
val s = Socket()
|
||||||
|
val stream = s.getInputStream()<caret>
|
||||||
|
}
|
||||||
@@ -14605,6 +14605,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("suppressedByNotPropertyList.kt")
|
||||||
|
public void testSuppressedByNotPropertyList() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/usePropertyAccessSyntax/suppressedByNotPropertyList.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("typeParameterReceiver.kt")
|
@TestMetadata("typeParameterReceiver.kt")
|
||||||
public void testTypeParameterReceiver() throws Exception {
|
public void testTypeParameterReceiver() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/usePropertyAccessSyntax/typeParameterReceiver.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/usePropertyAccessSyntax/typeParameterReceiver.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user