All operator usages are searched with new searcher

This commit is contained in:
Valentin Kipyatkov
2016-09-27 18:26:39 +03:00
parent ba76e4d24d
commit af0b027952
12 changed files with 40 additions and 130 deletions
@@ -58,9 +58,8 @@ import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE;
import static org.jetbrains.kotlin.types.TypeUtils.noExpectedType;
import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.createFakeExpressionOfType;
//TODO: check for 'operator' modifier!
public class DelegatedPropertyResolver {
public static final Name PROPERTY_DELEGATED_FUNCTION_NAME = Name.identifier("propertyDelegated");
private final KotlinBuiltIns builtIns;
private final FakeCallResolver fakeCallResolver;
private final ExpressionTypingServices expressionTypingServices;
@@ -198,7 +197,7 @@ public class DelegatedPropertyResolver {
ExpressionReceiver receiver = ExpressionReceiver.Companion.create(delegateExpression, delegateType, trace.getBindingContext());
Pair<Call, OverloadResolutionResults<FunctionDescriptor>> resolutionResult =
fakeCallResolver.makeAndResolveFakeCallInContext(receiver, context, arguments, PROPERTY_DELEGATED_FUNCTION_NAME, delegateExpression);
fakeCallResolver.makeAndResolveFakeCallInContext(receiver, context, arguments, OperatorNameConventions.PROPERTY_DELEGATED, delegateExpression);
Call call = resolutionResult.getFirst();
OverloadResolutionResults<FunctionDescriptor> functionResults = resolutionResult.getSecond();
@@ -59,6 +59,8 @@ object OperatorNameConventions {
@JvmField val COROUTINE_HANDLE_RESULT = Name.identifier("handleResult")
@JvmField val COROUTINE_HANDLE_EXCEPTION = Name.identifier("handleException")
@JvmField val PROPERTY_DELEGATED = Name.identifier("propertyDelegated")
// If you add new unary, binary or assignment operators, add it to OperatorConventions as well
@JvmField
@@ -46,6 +46,7 @@ import org.jetbrains.kotlin.util.OperatorNameConventions.INC
import org.jetbrains.kotlin.util.OperatorNameConventions.INVOKE
import org.jetbrains.kotlin.util.OperatorNameConventions.ITERATOR
import org.jetbrains.kotlin.util.OperatorNameConventions.NEXT
import org.jetbrains.kotlin.util.OperatorNameConventions.PROPERTY_DELEGATED
import org.jetbrains.kotlin.util.OperatorNameConventions.RANGE_TO
import org.jetbrains.kotlin.util.OperatorNameConventions.SET
import org.jetbrains.kotlin.util.OperatorNameConventions.SET_VALUE
@@ -215,7 +216,8 @@ object OperatorChecks : AbstractModifierChecks() {
?: ensure(valueParameters[0].type.isThrowable()) {
"First parameter should be 'Throwable'"
}
}
},
Checks(PROPERTY_DELEGATED, Member, ValueParameterCountCheck.Equals(1)) //TODO: more checks required!
)
private fun FunctionDescriptor.checkHandleSecondParameter(): String? {
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.idea.search
import com.google.common.collect.ImmutableSet
import com.intellij.lexer.Lexer
import com.intellij.psi.TokenType
import com.intellij.psi.impl.cache.impl.BaseFilterLexer
@@ -26,14 +27,27 @@ import com.intellij.psi.impl.cache.impl.todo.LexerBasedTodoIndexer
import com.intellij.psi.search.UsageSearchContext
import com.intellij.psi.tree.IElementType
import com.intellij.psi.tree.TokenSet
import org.jetbrains.kotlin.idea.search.usagesSearch.ALL_SEARCHABLE_OPERATIONS
import org.jetbrains.kotlin.kdoc.lexer.KDocTokens
import org.jetbrains.kotlin.lexer.KotlinLexer
import org.jetbrains.kotlin.lexer.KtToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import java.util.*
val KOTLIN_NAMED_ARGUMENT_SEARCH_CONTEXT: Short = 0x20
private val ALL_SEARCHABLE_OPERATIONS: ImmutableSet<KtToken> = ImmutableSet
.builder<KtToken>()
.addAll(OperatorConventions.UNARY_OPERATION_NAMES.keys)
.addAll(OperatorConventions.BINARY_OPERATION_NAMES.keys)
.addAll(OperatorConventions.ASSIGNMENT_OPERATIONS.keys)
.addAll(OperatorConventions.COMPARISON_OPERATIONS)
.addAll(OperatorConventions.EQUALS_OPERATIONS)
.addAll(OperatorConventions.IN_OPERATIONS)
.add(KtTokens.LBRACKET)
.add(KtTokens.BY_KEYWORD)
.build()
class KotlinFilterLexer(private val occurrenceConsumer: OccurrenceConsumer): BaseFilterLexer(KotlinLexer(), occurrenceConsumer) {
private val codeTokens = TokenSet.orSet(
TokenSet.create(*ALL_SEARCHABLE_OPERATIONS.toTypedArray()),
@@ -39,13 +39,11 @@ import org.jetbrains.kotlin.idea.search.unionSafe
import org.jetbrains.kotlin.idea.search.usagesSearch.OperatorReferenceSearcher
import org.jetbrains.kotlin.idea.search.usagesSearch.dataClassComponentFunction
import org.jetbrains.kotlin.idea.search.usagesSearch.getClassNameForCompanionObject
import org.jetbrains.kotlin.idea.search.usagesSearch.getSpecialNamesToSearch
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.utils.singletonOrEmptyList
import java.util.*
data class KotlinReferencesSearchOptions(val acceptCallableOverrides: Boolean = false,
@@ -94,9 +92,6 @@ class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, ReferencesSearc
val unwrappedElement = element.namedUnwrappedElement ?: return
val specialSymbols = unwrappedElement.getSpecialNamesToSearch(kotlinOptions)
val words = (specialSymbols?.first ?: emptyList()) + unwrappedElement.getClassNameForCompanionObject().singletonOrEmptyList()
val elements = if (unwrappedElement is KtDeclaration) unwrappedElement.toLightElements() else listOf(unwrappedElement)
val effectiveSearchScope = elements.fold(queryParameters.effectiveSearchScope) { scope, e ->
scope.unionSafe(queryParameters.effectiveSearchScope(e))
@@ -104,7 +99,6 @@ class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, ReferencesSearc
val refFilter: (PsiReference) -> Boolean = when {
unwrappedElement is KtParameter -> ({ ref: PsiReference -> !ref.isNamedArgumentReference()/* they are processed later*/ })
specialSymbols != null -> { ref -> ref.javaClass == specialSymbols.second }
else -> ({true})
}
@@ -117,9 +111,12 @@ class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, ReferencesSearc
name, effectiveSearchScope, UsageSearchContext.IN_CODE, true, unwrappedElement, resultProcessor)
}
}
words.forEach { word ->
val classNameForCompanionObject = unwrappedElement.getClassNameForCompanionObject()
if (classNameForCompanionObject != null) {
queryParameters.optimizer.searchWord(
word, effectiveSearchScope, UsageSearchContext.ANY, true, unwrappedElement, resultProcessor)
classNameForCompanionObject, effectiveSearchScope, UsageSearchContext.ANY, true, unwrappedElement, resultProcessor)
}
if (unwrappedElement is KtParameter && kotlinOptions.searchNamedArguments) {
@@ -31,11 +31,9 @@ import com.intellij.util.Processor
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.KotlinLanguage
@@ -53,9 +51,7 @@ import org.jetbrains.kotlin.idea.util.FuzzyType
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.kdoc.psi.impl.KDocName
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.sam.SingleAbstractMethodUtils
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
@@ -207,11 +203,6 @@ class ExpressionsOfTypeProcessor(
}
private fun addCallableDeclarationToProcess(declaration: PsiElement, scope: SearchScope, processMethod: (PsiReference) -> Boolean) {
if (declaration.isOperatorExpensiveToSearch()) { // cancel all tasks and use plain search
downShiftToPlainSearch()
return
}
data class ProcessCallableUsagesTask(val declaration: PsiElement, val processMethod: (PsiReference) -> Boolean) : Task {
override fun perform() {
testLog?.add("Searched references to ${logPresentation(declaration)} in non-Java files")
@@ -661,25 +652,6 @@ class ExpressionsOfTypeProcessor(
private fun PsiModifierListOwner.isLocal() = parents.any { it is PsiCodeBlock }
private fun PsiElement.isOperatorExpensiveToSearch(): Boolean {
when (this) {
is KtFunction -> {
val isOperator = hasModifier(KtTokens.OPERATOR_KEYWORD)
|| hasModifier(KtTokens.OVERRIDE_KEYWORD) && (resolveToDescriptorIfAny() as? FunctionDescriptor)?.isOperator == true
val name = name
return isOperator && name != null && Name.isValidIdentifier(name) && Name.identifier(name).getOperationSymbolsToSearch() != null
}
is KtLightMethod -> {
return kotlinOrigin?.isOperatorExpensiveToSearch() == true
}
else -> {
return false
}
}
}
private fun KotlinType.containsTypeOrDerivedInside(type: FuzzyType): Boolean {
return type.checkIsSuperTypeOf(this) != null || arguments.any { it.type.containsTypeOrDerivedInside(type) }
}
@@ -183,7 +183,7 @@ abstract class OperatorReferenceSearcher<TReferenceElement : KtElement>(
name == OperatorNameConventions.ITERATOR ->
return IteratorOperatorReferenceSearcher(declaration, searchScope, consumer, optimizer, options)
name == OperatorNameConventions.GET_VALUE || name == OperatorNameConventions.SET_VALUE ->
name == OperatorNameConventions.GET_VALUE || name == OperatorNameConventions.SET_VALUE || name == OperatorNameConventions.PROPERTY_DELEGATED ->
return PropertyDelegationOperatorReferenceSearcher(declaration, searchScope, consumer, optimizer, options)
else ->
@@ -1,45 +0,0 @@
/*
* Copyright 2010-2015 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.search.usagesSearch
import com.google.common.collect.ImmutableSet
import org.jetbrains.kotlin.idea.references.KtPropertyDelegationMethodsReference
import org.jetbrains.kotlin.lexer.KtToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DelegatedPropertyResolver
import org.jetbrains.kotlin.types.expressions.OperatorConventions.*
val ALL_SEARCHABLE_OPERATIONS: ImmutableSet<KtToken> = ImmutableSet
.builder<KtToken>()
.addAll(UNARY_OPERATION_NAMES.keys)
.addAll(BINARY_OPERATION_NAMES.keys)
.addAll(ASSIGNMENT_OPERATIONS.keys)
.addAll(COMPARISON_OPERATIONS)
.addAll(EQUALS_OPERATIONS)
.addAll(IN_OPERATIONS)
.add(KtTokens.LBRACKET)
.add(KtTokens.BY_KEYWORD)
.build()
fun Name.getOperationSymbolsToSearch(): Pair<Set<KtToken>, Class<*>>? {
when (this) {
DelegatedPropertyResolver.PROPERTY_DELEGATED_FUNCTION_NAME -> return setOf(KtTokens.BY_KEYWORD) to KtPropertyDelegationMethodsReference::class.java
}
return null
}
@@ -22,9 +22,6 @@ import org.jetbrains.kotlin.asJava.LightClassUtil.PropertyAccessorsPsiMethods
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
import org.jetbrains.kotlin.lexer.KtSingleValueToken
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.resolve.BindingContext
@@ -63,18 +60,6 @@ fun PsiNamedElement.getClassNameForCompanionObject(): String? {
}
}
fun PsiNamedElement.getSpecialNamesToSearch(options: KotlinReferencesSearchOptions): Pair<List<String>, Class<*>>? {
val name = name
when {
name == null || !Name.isValidIdentifier(name) -> return null
else -> {
val operationSymbolsToSearch = Name.identifier(name).getOperationSymbolsToSearch() ?: return null
return operationSymbolsToSearch.first.map { (it as KtSingleValueToken).value } to operationSymbolsToSearch.second
}
}
}
fun KtParameter.dataClassComponentFunction(): FunctionDescriptor? {
if (!hasValOrVar()) return null
@@ -18,39 +18,19 @@ package org.jetbrains.kotlin.idea.search.ideaExtensions
import com.intellij.openapi.application.QueryExecutorBase
import com.intellij.psi.PsiReference
import com.intellij.psi.search.UsageSearchContext
import com.intellij.psi.search.searches.MethodReferencesSearch
import com.intellij.util.Processor
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
import org.jetbrains.kotlin.idea.search.usagesSearch.OperatorReferenceSearcher
import org.jetbrains.kotlin.idea.search.usagesSearch.getOperationSymbolsToSearch
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.lexer.KtSingleValueToken
import org.jetbrains.kotlin.name.Name
class KotlinConventionMethodReferencesSearcher() : QueryExecutorBase<PsiReference, MethodReferencesSearch.SearchParameters>(true) {
override fun processQuery(queryParameters: MethodReferencesSearch.SearchParameters, consumer: Processor<PsiReference>) {
val method = queryParameters.method
val name = runReadAction { method.name }
if (!Name.isValidIdentifier(name)) return
val identifier = Name.identifier(name)
val operatorSearcher = OperatorReferenceSearcher.create(
method, queryParameters.effectiveSearchScope, consumer, queryParameters.optimizer, KotlinReferencesSearchOptions(acceptCallableOverrides = true))
if (operatorSearcher != null) {
operatorSearcher.run()
}
else {
val operationSymbolsToSearch = identifier.getOperationSymbolsToSearch() ?: return
val wordsToSearch = operationSymbolsToSearch.first.map { (it as KtSingleValueToken).value }
val resultProcessor = KotlinRequestResultProcessor(method,
filter = { ref -> ref.javaClass == operationSymbolsToSearch.second })
wordsToSearch.forEach { word ->
queryParameters.optimizer.searchWord(word, queryParameters.effectiveSearchScope.restrictToKotlinSources(),
UsageSearchContext.IN_CODE, true, method,
resultProcessor)
}
}
queryParameters.method,
queryParameters.effectiveSearchScope,
consumer,
queryParameters.optimizer,
KotlinReferencesSearchOptions(acceptCallableOverrides = true)
)
operatorSearcher?.run()
}
}
@@ -9,7 +9,7 @@ class Delegate() {
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
}
fun <caret>propertyDelegated(property: KProperty<*>) {
operator fun <caret>propertyDelegated(property: KProperty<*>) {
}
}
@@ -0,0 +1,4 @@
Resolved by Delegate()
Searched references to Delegate
Used plain search of Delegate.propertyDelegated(property: KProperty<*>) in LocalSearchScope:
CLASS:Delegate