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.util.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
@@ -49,7 +50,8 @@ class ReferenceVariantsHelper(
|
||||
private val bindingContext: BindingContext,
|
||||
private val resolutionFacade: ResolutionFacade,
|
||||
private val moduleDescriptor: ModuleDescriptor,
|
||||
private val visibilityFilter: (DeclarationDescriptor) -> Boolean
|
||||
private val visibilityFilter: (DeclarationDescriptor) -> Boolean,
|
||||
private val notProperties: Set<FqNameUnsafe> = setOf()
|
||||
) {
|
||||
fun getReferenceVariants(
|
||||
expression: KtSimpleNameExpression,
|
||||
@@ -96,7 +98,9 @@ class ReferenceVariantsHelper(
|
||||
|
||||
fun filterOutJavaGettersAndSetters(variants: Collection<DeclarationDescriptor>): Collection<DeclarationDescriptor> {
|
||||
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) {
|
||||
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
|
||||
|
||||
@@ -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="gradle-and-groovy-plugin" level="project" />
|
||||
<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">
|
||||
<library>
|
||||
<CLASSES>
|
||||
|
||||
+4
-2
@@ -78,7 +78,9 @@ abstract class IntentionBasedInspection<TElement : PsiElement>(
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
|
||||
|
||||
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()
|
||||
?: error("$intentionInfos should have the same elementType")
|
||||
@@ -105,7 +107,7 @@ abstract class IntentionBasedInspection<TElement : PsiElement>(
|
||||
if (fixes == null) {
|
||||
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)
|
||||
}
|
||||
|
||||
var inspection: IntentionBasedInspection<TElement>? = null
|
||||
internal set
|
||||
|
||||
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)
|
||||
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 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 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);
|
||||
}
|
||||
|
||||
@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")
|
||||
public void testSyntheticExtensions1() throws Exception {
|
||||
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.completion.BasicLookupElementFactory
|
||||
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.util.CallType
|
||||
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
|
||||
@@ -60,7 +61,7 @@ abstract class BaseKotlinVariableMacro<TState> : Macro() {
|
||||
|
||||
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
|
||||
.getReferenceVariants(contextElement, CallTypeAndReceiver.DEFAULT, DescriptorKindFilter.VARIABLES, { true })
|
||||
.map { it as VariableDescriptor }
|
||||
|
||||
@@ -119,6 +119,11 @@
|
||||
<add-to-group group-id="KotlinToolsGroup" anchor="last"/>
|
||||
</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"
|
||||
text="Find Implicit Nothing Calls">
|
||||
<add-to-group group-id="KotlinToolsGroup" anchor="last"/>
|
||||
@@ -306,6 +311,9 @@
|
||||
<projectService serviceInterface="org.jetbrains.kotlin.config.KotlinFacetSettingsProvider"
|
||||
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"/>
|
||||
|
||||
<internalFileTemplate name="Kotlin File"/>
|
||||
@@ -1612,6 +1620,7 @@
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.UsePropertyAccessSyntaxInspection"
|
||||
displayName="Accessor call that can be replaced with property access syntax"
|
||||
shortName="UsePropertyAccessSyntax"
|
||||
groupName="Kotlin"
|
||||
cleanupTool="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.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.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Severity
|
||||
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
|
||||
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.replaced
|
||||
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.resolve.frontendService
|
||||
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.psi.*
|
||||
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.resolve.BindingContext
|
||||
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.isUsedAsExpression
|
||||
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.TypeUtils
|
||||
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") {
|
||||
override fun isApplicableTo(element: KtCallExpression): Boolean {
|
||||
@@ -87,6 +141,11 @@ class UsePropertyAccessSyntaxIntention : SelfTargetingOffsetIndependentIntention
|
||||
if (!resolvedCall.isReallySuccess()) 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 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.idea.caches.resolve.getResolutionFacade
|
||||
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.quickfix.createFromUsage.callableBuilder.guessTypes
|
||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.KotlinPsiUnifier
|
||||
@@ -101,9 +102,9 @@ class RenameUnresolvedReferenceFix(element: KtNameReferenceExpression): KotlinQu
|
||||
val resolutionFacade = element.getResolutionFacade()
|
||||
val context = resolutionFacade.analyze(element, BodyResolveMode.PARTIAL)
|
||||
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)
|
||||
}
|
||||
}, NotPropertiesService.getNotProperties(element))
|
||||
val expectedTypes = patternExpression
|
||||
.guessTypes(context, moduleDescriptor)
|
||||
.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);
|
||||
}
|
||||
|
||||
@TestMetadata("suppressedByNotPropertyList.kt")
|
||||
public void testSuppressedByNotPropertyList() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/usePropertyAccessSyntax/suppressedByNotPropertyList.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("typeParameterReceiver.kt")
|
||||
public void testTypeParameterReceiver() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/usePropertyAccessSyntax/typeParameterReceiver.kt");
|
||||
|
||||
Reference in New Issue
Block a user