[FIR IDE] Separate find usages logic from descriptors
This commit is contained in:
@@ -79,7 +79,6 @@ wrapped.into.a.reference.object.to.be.modified.when.captured.in.a.closure=Wrappe
|
||||
smart.cast.to.0.for.1.call=Smart cast to {0} (for {1} call)
|
||||
smart.cast.to.0=Smart cast to {0}
|
||||
replace.overloaded.operator.with.function.call=Replace overloaded operator with function call
|
||||
searching.for.implicit.usages=Searching for implicit usages...
|
||||
class.initializer=<class initializer>
|
||||
object.0=object{0}
|
||||
show.non.public=Show non-public
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.search
|
||||
|
||||
import com.intellij.psi.impl.search.IndexPatternBuilder
|
||||
import com.intellij.psi.tree.IElementType
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
|
||||
abstract class IndexPatternBuilderAdapter : IndexPatternBuilder {
|
||||
override fun getCommentStartDelta(tokenType: IElementType, tokenText: CharSequence): Int {
|
||||
return when (tokenType) {
|
||||
KtTokens.EOL_COMMENT -> 2
|
||||
KtTokens.BLOCK_COMMENT -> 2
|
||||
KtTokens.DOC_COMMENT -> 3
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
override fun getCharsAllowedInContinuationPrefix(tokenType: IElementType): String {
|
||||
return when (tokenType) {
|
||||
KtTokens.BLOCK_COMMENT -> "*"
|
||||
KtTokens.DOC_COMMENT -> "*"
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +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
|
||||
|
||||
import com.intellij.lexer.Lexer
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.tree.IElementType
|
||||
import com.intellij.psi.tree.TokenSet
|
||||
import org.jetbrains.kotlin.kdoc.lexer.KDocTokens
|
||||
import org.jetbrains.kotlin.lexer.KotlinLexer
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
class KotlinIndexPatternBuilder : IndexPatternBuilderAdapter() {
|
||||
private companion object {
|
||||
private val TODO_COMMENT_TOKENS = TokenSet.orSet(KtTokens.COMMENTS, TokenSet.create(KDocTokens.KDOC))
|
||||
}
|
||||
|
||||
override fun getCommentTokenSet(file: PsiFile): TokenSet? {
|
||||
return if (file is KtFile) TODO_COMMENT_TOKENS else null
|
||||
}
|
||||
|
||||
override fun getIndexingLexer(file: PsiFile): Lexer? {
|
||||
return if (file is KtFile) KotlinLexer() else null
|
||||
}
|
||||
|
||||
override fun getCommentStartDelta(tokenType: IElementType?): Int = 0
|
||||
|
||||
override fun getCommentEndDelta(tokenType: IElementType?): Int = when (tokenType) {
|
||||
KtTokens.BLOCK_COMMENT -> "*/".length
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
@@ -1,149 +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
|
||||
|
||||
import com.google.common.collect.ImmutableSet
|
||||
import com.intellij.lexer.Lexer
|
||||
import com.intellij.psi.TokenType
|
||||
import com.intellij.psi.impl.cache.impl.BaseFilterLexer
|
||||
import com.intellij.psi.impl.cache.impl.IdAndToDoScannerBasedOnFilterLexer
|
||||
import com.intellij.psi.impl.cache.impl.OccurrenceConsumer
|
||||
import com.intellij.psi.impl.cache.impl.id.LexerBasedIdIndexer
|
||||
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.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.*
|
||||
|
||||
const 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 companion object {
|
||||
private val CODE_TOKENS = TokenSet.orSet(
|
||||
TokenSet.create(*ALL_SEARCHABLE_OPERATIONS.toTypedArray()),
|
||||
TokenSet.create(KtTokens.IDENTIFIER)
|
||||
)
|
||||
|
||||
private val COMMENT_TOKENS = TokenSet.orSet(KtTokens.COMMENTS, TokenSet.create(KDocTokens.KDOC))
|
||||
private const val MAX_PREV_TOKENS = 2
|
||||
}
|
||||
|
||||
private val prevTokens = ArrayDeque<IElementType>(MAX_PREV_TOKENS)
|
||||
|
||||
private var prevTokenStart = -1
|
||||
private var prevTokenEnd = -1
|
||||
|
||||
override fun advance() {
|
||||
val tokenType = myDelegate.tokenType
|
||||
|
||||
when (tokenType) {
|
||||
KtTokens.EQ -> {
|
||||
if (prevTokens.peekFirst() == KtTokens.IDENTIFIER) {
|
||||
val prevPrev = prevTokens.elementAtOrNull(1)
|
||||
if (prevPrev == KtTokens.COMMA || prevPrev == KtTokens.LPAR) {
|
||||
occurrenceConsumer.addOccurrence(
|
||||
bufferSequence,
|
||||
null,
|
||||
prevTokenStart,
|
||||
prevTokenEnd,
|
||||
KOTLIN_NAMED_ARGUMENT_SEARCH_CONTEXT.toInt()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KtTokens.LPAR -> {
|
||||
if (isMultiDeclarationPosition()) {
|
||||
addOccurrenceInToken(UsageSearchContext.IN_CODE.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
KtTokens.IDENTIFIER -> {
|
||||
if (myDelegate.tokenText.startsWith("`")) {
|
||||
scanWordsInToken(UsageSearchContext.IN_CODE.toInt(), false, false)
|
||||
} else {
|
||||
addOccurrenceInToken(UsageSearchContext.IN_CODE.toInt())
|
||||
if (myDelegate.tokenText == "TODO") {
|
||||
// Heuristics to reduce mismatches between indexer and searcher. The searcher returns only occurrences of TO_DO
|
||||
// as the callee of a call expression, but we can't tell calls and other usages apart based on limited lexer context,
|
||||
// so we just exclude occurrences in declaration names (and even that doesn't work precisely because it doesn't handle
|
||||
// declarations with type parameters)
|
||||
val prevToken = prevTokens.peekFirst()
|
||||
if (prevToken != KtTokens.FUN_KEYWORD && prevToken != KtTokens.VAR_KEYWORD && prevToken != KtTokens.VAL_KEYWORD && prevToken != KtTokens.CLASS_KEYWORD) {
|
||||
advanceTodoItemCountsInToken()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
in CODE_TOKENS -> addOccurrenceInToken(UsageSearchContext.IN_CODE.toInt())
|
||||
|
||||
in KtTokens.STRINGS -> scanWordsInToken(UsageSearchContext.IN_STRINGS + UsageSearchContext.IN_FOREIGN_LANGUAGES, false, true)
|
||||
|
||||
in COMMENT_TOKENS -> {
|
||||
scanWordsInToken(UsageSearchContext.IN_COMMENTS.toInt(), false, false)
|
||||
advanceTodoItemCountsInToken()
|
||||
}
|
||||
}
|
||||
|
||||
if (tokenType != TokenType.WHITE_SPACE && tokenType !in COMMENT_TOKENS) {
|
||||
if (prevTokens.size == MAX_PREV_TOKENS) {
|
||||
prevTokens.removeLast()
|
||||
}
|
||||
prevTokens.addFirst(tokenType)
|
||||
prevTokenStart = tokenStart
|
||||
prevTokenEnd = tokenEnd
|
||||
}
|
||||
|
||||
myDelegate.advance()
|
||||
}
|
||||
|
||||
private fun isMultiDeclarationPosition(): Boolean {
|
||||
val first = prevTokens.peekFirst()
|
||||
if (first == KtTokens.VAL_KEYWORD || first == KtTokens.VAR_KEYWORD) return true
|
||||
return first == KtTokens.LPAR && prevTokens.elementAtOrNull(1) == KtTokens.FOR_KEYWORD
|
||||
}
|
||||
}
|
||||
|
||||
class KotlinIdIndexer : LexerBasedIdIndexer() {
|
||||
override fun createLexer(consumer: OccurrenceConsumer): Lexer = KotlinFilterLexer(consumer)
|
||||
|
||||
override fun getVersion() = 3
|
||||
}
|
||||
|
||||
class KotlinTodoIndexer : LexerBasedTodoIndexer(), IdAndToDoScannerBasedOnFilterLexer {
|
||||
override fun getVersion() = 2
|
||||
|
||||
override fun createLexer(consumer: OccurrenceConsumer) = KotlinFilterLexer(consumer)
|
||||
}
|
||||
@@ -1,255 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.search
|
||||
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiModifier
|
||||
import com.intellij.psi.search.PsiShortNamesCache
|
||||
import com.intellij.psi.util.CachedValue
|
||||
import com.intellij.psi.util.CachedValueProvider
|
||||
import com.intellij.psi.util.CachedValuesManager
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.asJava.ImpreciseResolveResult
|
||||
import org.jetbrains.kotlin.asJava.ImpreciseResolveResult.*
|
||||
import org.jetbrains.kotlin.idea.caches.project.getNullableModuleInfo
|
||||
import org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListener
|
||||
import org.jetbrains.kotlin.idea.compiler.IDELanguageSettingsProvider
|
||||
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
|
||||
import org.jetbrains.kotlin.idea.project.findAnalyzerServices
|
||||
import org.jetbrains.kotlin.idea.stubindex.KotlinTypeAliasShortNameIndex
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.resolve.ImportPath
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
/**
|
||||
* Can quickly check whether a short name reference in a given file can resolve to the class/interface/type alias
|
||||
* with the given qualified name.
|
||||
*/
|
||||
class PsiBasedClassResolver @TestOnly constructor(private val targetClassFqName: String) {
|
||||
private val targetShortName = targetClassFqName.substringAfterLast('.')
|
||||
private val targetPackage = targetClassFqName.substringBeforeLast('.', "")
|
||||
|
||||
/**
|
||||
* Qualified names of packages which contain classes with the same short name as the target class.
|
||||
*/
|
||||
private val conflictingPackages = mutableListOf<String>()
|
||||
|
||||
/**
|
||||
* Qualified names of packages which contain typealiases with the same short name as the target class
|
||||
* (which may or may not resolve to the target class).
|
||||
*/
|
||||
private val packagesWithTypeAliases = mutableListOf<String>()
|
||||
private var forceAmbiguity: Boolean = false
|
||||
private var forceAmbiguityForInnerAnnotations: Boolean = false
|
||||
private var forceAmbiguityForNonAnnotations: Boolean = false
|
||||
|
||||
companion object {
|
||||
@get:TestOnly
|
||||
val attempts = AtomicInteger()
|
||||
|
||||
@get:TestOnly
|
||||
val trueHits = AtomicInteger()
|
||||
|
||||
@get:TestOnly
|
||||
val falseHits = AtomicInteger()
|
||||
|
||||
private val PSI_BASED_CLASS_RESOLVER_KEY = Key<CachedValue<PsiBasedClassResolver>>("PsiBasedClassResolver")
|
||||
|
||||
fun getInstance(target: PsiClass): PsiBasedClassResolver {
|
||||
target.getUserData(PSI_BASED_CLASS_RESOLVER_KEY)?.let { return it.value }
|
||||
|
||||
val cachedValue = CachedValuesManager.getManager(target.project).createCachedValue(
|
||||
{
|
||||
CachedValueProvider.Result(
|
||||
PsiBasedClassResolver(target),
|
||||
KotlinCodeBlockModificationListener.getInstance(target.project).kotlinOutOfCodeBlockTracker
|
||||
)
|
||||
}, false
|
||||
)
|
||||
|
||||
target.putUserData(PSI_BASED_CLASS_RESOLVER_KEY, cachedValue)
|
||||
|
||||
return cachedValue.value
|
||||
}
|
||||
}
|
||||
|
||||
private constructor(target: PsiClass) : this(target.qualifiedName ?: "") {
|
||||
if (target.qualifiedName == null || target.containingClass != null || targetPackage.isEmpty()) {
|
||||
forceAmbiguity = true
|
||||
return
|
||||
}
|
||||
|
||||
runReadAction {
|
||||
findPotentialClassConflicts(target)
|
||||
findPotentialTypeAliasConflicts(target)
|
||||
}
|
||||
}
|
||||
|
||||
private fun findPotentialClassConflicts(target: PsiClass) {
|
||||
val candidates = PsiShortNamesCache.getInstance(target.project).getClassesByName(targetShortName, target.project.allScope())
|
||||
for (candidate in candidates) {
|
||||
// An inner class can be referenced by short name in subclasses without an explicit import
|
||||
if (candidate.containingClass != null && !candidate.hasModifierProperty(PsiModifier.PRIVATE)) {
|
||||
if (candidate.isAnnotationType) {
|
||||
forceAmbiguityForInnerAnnotations = true
|
||||
} else {
|
||||
forceAmbiguityForNonAnnotations = true
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if (candidate.qualifiedName == target.qualifiedName) {
|
||||
// File with same FQ name in another module, don't bother with analyzing dependencies
|
||||
if (candidate.navigationElement.containingFile != target.navigationElement.containingFile) {
|
||||
forceAmbiguity = true
|
||||
break
|
||||
}
|
||||
} else {
|
||||
candidate.qualifiedName?.substringBeforeLast('.', "")?.let { candidatePackage ->
|
||||
if (candidatePackage == "")
|
||||
forceAmbiguity = true
|
||||
else
|
||||
conflictingPackages.add(candidatePackage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun findPotentialTypeAliasConflicts(target: PsiClass) {
|
||||
val candidates = KotlinTypeAliasShortNameIndex.getInstance().get(targetShortName, target.project, target.project.allScope())
|
||||
for (candidate in candidates) {
|
||||
packagesWithTypeAliases.add(candidate.containingKtFile.packageFqName.asString())
|
||||
}
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun addConflict(fqName: String) {
|
||||
conflictingPackages.add(fqName.substringBeforeLast('.'))
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a reference with the short name of [targetClassFqName] in the given file will resolve
|
||||
* to the target class.
|
||||
*
|
||||
* @return true if it will definitely resolve to that class, false if it will definitely resolve to something else,
|
||||
* null if full resolve is required to answer that question.
|
||||
*/
|
||||
fun canBeTargetReference(ref: KtSimpleNameExpression): ImpreciseResolveResult {
|
||||
attempts.incrementAndGet()
|
||||
// The names can be different if the target was imported via an import alias
|
||||
if (ref.getReferencedName() != targetShortName) {
|
||||
return UNSURE
|
||||
}
|
||||
|
||||
// Names in expressions can conflict with local declarations and methods of implicit receivers,
|
||||
// so we can't find out what they refer to without a full resolve.
|
||||
val userType = ref.getStrictParentOfType<KtUserType>() ?: return UNSURE
|
||||
val parentAnnotation = userType.getParentOfTypeAndBranch<KtAnnotationEntry> { typeReference }
|
||||
if (forceAmbiguityForNonAnnotations && parentAnnotation == null) return UNSURE
|
||||
|
||||
//For toplevel declarations it's fine to resolve by imports
|
||||
val declaration = parentAnnotation?.getParentOfType<KtDeclaration>(true)
|
||||
if (forceAmbiguityForInnerAnnotations && declaration?.parent !is KtFile) return UNSURE
|
||||
if (forceAmbiguity) return UNSURE
|
||||
|
||||
val qualifiedCheckResult = checkQualifiedReferenceToTarget(ref)
|
||||
if (qualifiedCheckResult != null) return qualifiedCheckResult.returnValue
|
||||
|
||||
val file = ref.containingKtFile
|
||||
var result: Result = Result.NothingFound
|
||||
when (file.packageFqName.asString()) {
|
||||
targetPackage -> result = result.changeTo(Result.Found)
|
||||
in conflictingPackages -> result = result.changeTo(Result.FoundOther)
|
||||
in packagesWithTypeAliases -> return UNSURE
|
||||
}
|
||||
|
||||
for (importPath in file.getDefaultImports()) {
|
||||
result = analyzeSingleImport(result, importPath.fqName, importPath.isAllUnder, importPath.alias?.asString())
|
||||
if (result == Result.Ambiguity) return UNSURE
|
||||
}
|
||||
|
||||
for (importDirective in file.importDirectives) {
|
||||
result = analyzeSingleImport(result, importDirective.importedFqName, importDirective.isAllUnder, importDirective.aliasName)
|
||||
if (result == Result.Ambiguity) return UNSURE
|
||||
}
|
||||
|
||||
if (result.returnValue == MATCH) {
|
||||
trueHits.incrementAndGet()
|
||||
} else if (result.returnValue == NO_MATCH) {
|
||||
falseHits.incrementAndGet()
|
||||
}
|
||||
return result.returnValue
|
||||
}
|
||||
|
||||
private fun analyzeSingleImport(result: Result, importedFqName: FqName?, isAllUnder: Boolean, aliasName: String?): Result {
|
||||
if (!isAllUnder) {
|
||||
if (importedFqName?.asString() == targetClassFqName &&
|
||||
(aliasName == null || aliasName == targetShortName)
|
||||
) {
|
||||
return result.changeTo(Result.Found)
|
||||
} else if (importedFqName?.shortName()?.asString() == targetShortName &&
|
||||
importedFqName.parent().asString() in conflictingPackages &&
|
||||
aliasName == null
|
||||
) {
|
||||
return result.changeTo(Result.FoundOther)
|
||||
} else if (importedFqName?.shortName()?.asString() == targetShortName &&
|
||||
importedFqName.parent().asString() in packagesWithTypeAliases &&
|
||||
aliasName == null
|
||||
) {
|
||||
return Result.Ambiguity
|
||||
} else if (aliasName == targetShortName) {
|
||||
return result.changeTo(Result.FoundOther)
|
||||
}
|
||||
} else {
|
||||
when {
|
||||
importedFqName?.asString() == targetPackage -> return result.changeTo(Result.Found)
|
||||
importedFqName?.asString() in conflictingPackages -> return result.changeTo(Result.FoundOther)
|
||||
importedFqName?.asString() in packagesWithTypeAliases -> return Result.Ambiguity
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun checkQualifiedReferenceToTarget(ref: KtSimpleNameExpression): Result? {
|
||||
// A qualified name can resolve to the target element even if it's not imported,
|
||||
// but it can also resolve to something else e.g. if the file defines a class with the same name
|
||||
// as the top-level package of the target class.
|
||||
val qualifier = (ref.parent as? KtUserType)?.qualifier
|
||||
if (qualifier != null) {
|
||||
if (qualifier.text == targetPackage) return Result.Ambiguity
|
||||
return Result.FoundOther
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
enum class Result(val returnValue: ImpreciseResolveResult) {
|
||||
NothingFound(NO_MATCH),
|
||||
Found(MATCH),
|
||||
FoundOther(NO_MATCH),
|
||||
Ambiguity(UNSURE)
|
||||
}
|
||||
|
||||
private fun Result.changeTo(newResult: Result): Result {
|
||||
if (this == Result.NothingFound || this.returnValue == newResult.returnValue) {
|
||||
return newResult
|
||||
}
|
||||
return Result.Ambiguity
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtFile.getDefaultImports(): List<ImportPath> {
|
||||
val moduleInfo = getNullableModuleInfo() ?: return emptyList()
|
||||
return TargetPlatformDetector.getPlatform(this).findAnalyzerServices(project).getDefaultImports(
|
||||
IDELanguageSettingsProvider.getLanguageVersionSettings(moduleInfo, project),
|
||||
includeLowPriorityImports = true
|
||||
)
|
||||
}
|
||||
-46
@@ -1,46 +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.declarationsSearch
|
||||
|
||||
import com.intellij.psi.PsiAnonymousClass
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiModifier
|
||||
import com.intellij.psi.search.searches.ClassInheritorsSearch
|
||||
import com.intellij.util.EmptyQuery
|
||||
import com.intellij.util.Query
|
||||
import org.jetbrains.kotlin.asJava.toLightClassWithBuiltinMapping
|
||||
import org.jetbrains.kotlin.idea.caches.lightClasses.KtFakeLightClass
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
|
||||
fun HierarchySearchRequest<*>.searchInheritors(): Query<PsiClass> {
|
||||
val psiClass: PsiClass = when (originalElement) {
|
||||
is KtClassOrObject -> runReadAction { originalElement.toLightClassWithBuiltinMapping() ?: KtFakeLightClass(originalElement) }
|
||||
is PsiClass -> originalElement
|
||||
else -> null
|
||||
} ?: return EmptyQuery.getEmptyQuery()
|
||||
|
||||
return ClassInheritorsSearch.search(
|
||||
psiClass,
|
||||
searchScope,
|
||||
searchDeeply,
|
||||
/* checkInheritance = */ true,
|
||||
/* includeAnonymous = */ true
|
||||
)
|
||||
}
|
||||
|
||||
fun PsiClass.isInheritable(): Boolean = !(this is PsiAnonymousClass || hasModifierProperty(PsiModifier.FINAL))
|
||||
-120
@@ -1,120 +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.declarationsSearch
|
||||
|
||||
import com.intellij.openapi.application.QueryExecutorBase
|
||||
import com.intellij.openapi.progress.ProgressIndicatorProvider
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.util.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.contains
|
||||
import java.util.*
|
||||
|
||||
interface DeclarationSearchRequest<in T> {
|
||||
val project: Project
|
||||
val searchScope: SearchScope
|
||||
}
|
||||
|
||||
interface SearchRequestWithElement<T : PsiElement> : DeclarationSearchRequest<T> {
|
||||
val originalElement: T
|
||||
override val project: Project get() = originalElement.project
|
||||
}
|
||||
|
||||
abstract class DeclarationsSearch<T : PsiElement, R : DeclarationSearchRequest<T>> : QueryFactory<T, R>() {
|
||||
init {
|
||||
registerExecutor(
|
||||
object : QueryExecutorBase<T, R>(true) {
|
||||
override fun processQuery(queryParameters: R, consumer: Processor<in T>) {
|
||||
doSearch(queryParameters, consumer)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
final override fun registerExecutor(executor: QueryExecutor<T, R>) {
|
||||
super.registerExecutor(executor)
|
||||
}
|
||||
|
||||
protected abstract fun doSearch(request: R, consumer: Processor<in T>)
|
||||
protected open fun isApplicable(request: R): Boolean = true
|
||||
|
||||
fun search(request: R): Query<T> = if (isApplicable(request)) createUniqueResultsQuery(request) else EmptyQuery.getEmptyQuery<T>()
|
||||
}
|
||||
|
||||
class HierarchySearchRequest<T : PsiElement>(
|
||||
override val originalElement: T,
|
||||
override val searchScope: SearchScope,
|
||||
val searchDeeply: Boolean = true
|
||||
) : SearchRequestWithElement<T> {
|
||||
fun <U : PsiElement> copy(newOriginalElement: U): HierarchySearchRequest<U> =
|
||||
HierarchySearchRequest(newOriginalElement, searchScope, searchDeeply)
|
||||
}
|
||||
|
||||
interface HierarchyTraverser<T> {
|
||||
fun nextElements(current: T): Iterable<T>
|
||||
fun shouldDescend(element: T): Boolean
|
||||
|
||||
fun forEach(initialElement: T, body: (T) -> Unit) {
|
||||
val stack = Stack<T>()
|
||||
val processed = HashSet<T>()
|
||||
|
||||
stack.push(initialElement)
|
||||
while (!stack.isEmpty()) {
|
||||
ProgressIndicatorProvider.checkCanceled()
|
||||
|
||||
val current = stack.pop()!!
|
||||
if (!processed.add(current)) continue
|
||||
|
||||
for (next in nextElements(current)) {
|
||||
ProgressIndicatorProvider.checkCanceled()
|
||||
|
||||
body(next)
|
||||
|
||||
if (shouldDescend(next)) {
|
||||
stack.push(next)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T : PsiElement> Processor<in T>.consumeHierarchy(request: SearchRequestWithElement<T>, traverser: HierarchyTraverser<T>) {
|
||||
traverser.forEach(request.originalElement) { element ->
|
||||
if (element in request.searchScope) {
|
||||
process(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class HierarchySearch<T : PsiElement>(
|
||||
private val traverser: HierarchyTraverser<T>
|
||||
) : DeclarationsSearch<T, HierarchySearchRequest<T>>() {
|
||||
protected open fun doSearchAll(request: HierarchySearchRequest<T>, consumer: Processor<in T>) {
|
||||
consumer.consumeHierarchy(request, traverser)
|
||||
}
|
||||
|
||||
protected abstract fun doSearchDirect(request: HierarchySearchRequest<T>, consumer: Processor<in T>)
|
||||
|
||||
override fun doSearch(request: HierarchySearchRequest<T>, consumer: Processor<in T>) {
|
||||
if (request.searchDeeply) {
|
||||
doSearchAll(request, consumer)
|
||||
} else {
|
||||
doSearchDirect(request, consumer)
|
||||
}
|
||||
}
|
||||
}
|
||||
-219
@@ -1,219 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.search.declarationsSearch
|
||||
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.psi.search.searches.AllOverridingMethodsSearch
|
||||
import com.intellij.psi.search.searches.DirectClassInheritorsSearch
|
||||
import com.intellij.psi.search.searches.FunctionalExpressionSearch
|
||||
import com.intellij.psi.search.searches.OverridingMethodsSearch
|
||||
import com.intellij.psi.util.MethodSignatureUtil
|
||||
import com.intellij.psi.util.PsiUtil
|
||||
import com.intellij.psi.util.TypeConversionUtil
|
||||
import com.intellij.util.EmptyQuery
|
||||
import com.intellij.util.MergeQuery
|
||||
import com.intellij.util.Processor
|
||||
import com.intellij.util.Query
|
||||
import org.jetbrains.kotlin.asJava.getRepresentativeLightMethod
|
||||
import org.jetbrains.kotlin.asJava.toLightMethods
|
||||
import org.jetbrains.kotlin.asJava.unwrapped
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.isOverridable
|
||||
import org.jetbrains.kotlin.idea.caches.lightClasses.KtFakeLightClass
|
||||
import org.jetbrains.kotlin.idea.caches.lightClasses.KtFakeLightMethod
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.core.getDeepestSuperDeclarations
|
||||
import org.jetbrains.kotlin.idea.core.isOverridable
|
||||
import org.jetbrains.kotlin.idea.search.allScope
|
||||
import org.jetbrains.kotlin.idea.search.excludeKotlinSources
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import org.jetbrains.kotlin.types.substitutions.getTypeSubstitutor
|
||||
import org.jetbrains.kotlin.util.findCallableMemberBySignature
|
||||
import java.util.*
|
||||
|
||||
fun PsiElement.isOverridableElement(): Boolean = when (this) {
|
||||
is PsiMethod -> PsiUtil.canBeOverridden(this)
|
||||
is KtDeclaration -> isOverridable()
|
||||
else -> false
|
||||
}
|
||||
|
||||
fun HierarchySearchRequest<*>.searchOverriders(): Query<PsiMethod> {
|
||||
val psiMethods = runReadAction { originalElement.toLightMethods() }
|
||||
if (psiMethods.isEmpty()) return EmptyQuery.getEmptyQuery()
|
||||
|
||||
return psiMethods
|
||||
.map { psiMethod -> KotlinPsiMethodOverridersSearch.search(copy(psiMethod)) }
|
||||
.reduce { query1, query2 -> MergeQuery(query1, query2) }
|
||||
}
|
||||
|
||||
object KotlinPsiMethodOverridersSearch : HierarchySearch<PsiMethod>(PsiMethodOverridingHierarchyTraverser) {
|
||||
fun searchDirectOverriders(psiMethod: PsiMethod): Iterable<PsiMethod> {
|
||||
fun PsiMethod.isAcceptable(inheritor: PsiClass, baseMethod: PsiMethod, baseClass: PsiClass): Boolean =
|
||||
when {
|
||||
hasModifierProperty(PsiModifier.STATIC) -> false
|
||||
baseMethod.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) ->
|
||||
JavaPsiFacade.getInstance(project).arePackagesTheSame(baseClass, inheritor)
|
||||
else -> true
|
||||
}
|
||||
|
||||
val psiClass = psiMethod.containingClass ?: return Collections.emptyList()
|
||||
|
||||
val classToMethod = LinkedHashMap<PsiClass, PsiMethod>()
|
||||
val classTraverser = object : HierarchyTraverser<PsiClass> {
|
||||
override fun nextElements(current: PsiClass): Iterable<PsiClass> =
|
||||
DirectClassInheritorsSearch.search(
|
||||
current,
|
||||
current.project.allScope(),
|
||||
/* includeAnonymous = */ true
|
||||
)
|
||||
|
||||
override fun shouldDescend(element: PsiClass): Boolean =
|
||||
element.isInheritable() && !classToMethod.containsKey(element)
|
||||
}
|
||||
|
||||
classTraverser.forEach(psiClass) { inheritor ->
|
||||
val substitutor = TypeConversionUtil.getSuperClassSubstitutor(psiClass, inheritor, PsiSubstitutor.EMPTY)
|
||||
val signature = psiMethod.getSignature(substitutor)
|
||||
val candidate = MethodSignatureUtil.findMethodBySuperSignature(inheritor, signature, false)
|
||||
if (candidate != null && candidate.isAcceptable(inheritor, psiMethod, psiClass)) {
|
||||
classToMethod[inheritor] = candidate
|
||||
}
|
||||
}
|
||||
|
||||
return classToMethod.values
|
||||
}
|
||||
|
||||
override fun isApplicable(request: HierarchySearchRequest<PsiMethod>): Boolean =
|
||||
runReadAction { request.originalElement.isOverridableElement() }
|
||||
|
||||
override fun doSearchDirect(request: HierarchySearchRequest<PsiMethod>, consumer: Processor<in PsiMethod>) {
|
||||
searchDirectOverriders(request.originalElement).forEach { method -> consumer.process(method) }
|
||||
}
|
||||
}
|
||||
|
||||
object PsiMethodOverridingHierarchyTraverser : HierarchyTraverser<PsiMethod> {
|
||||
override fun nextElements(current: PsiMethod): Iterable<PsiMethod> = KotlinPsiMethodOverridersSearch.searchDirectOverriders(current)
|
||||
override fun shouldDescend(element: PsiMethod): Boolean = PsiUtil.canBeOverridden(element)
|
||||
}
|
||||
|
||||
fun PsiElement.toPossiblyFakeLightMethods(): List<PsiMethod> {
|
||||
if (this is PsiMethod) return listOf(this)
|
||||
|
||||
val element = unwrapped ?: return emptyList()
|
||||
|
||||
val lightMethods = element.toLightMethods()
|
||||
if (lightMethods.isNotEmpty()) return lightMethods
|
||||
|
||||
return if (element is KtNamedDeclaration) listOfNotNull(KtFakeLightMethod.get(element)) else emptyList()
|
||||
}
|
||||
|
||||
private fun forEachKotlinOverride(
|
||||
ktClass: KtClass,
|
||||
members: List<KtNamedDeclaration>,
|
||||
scope: SearchScope,
|
||||
processor: (superMember: PsiElement, overridingMember: PsiElement) -> Boolean
|
||||
): Boolean {
|
||||
val baseClassDescriptor = runReadAction { ktClass.unsafeResolveToDescriptor() as ClassDescriptor }
|
||||
val baseDescriptors =
|
||||
runReadAction { members.mapNotNull { it.unsafeResolveToDescriptor() as? CallableMemberDescriptor }.filter { it.isOverridable } }
|
||||
if (baseDescriptors.isEmpty()) return true
|
||||
|
||||
HierarchySearchRequest(ktClass, scope, true).searchInheritors().forEach(Processor { psiClass ->
|
||||
val inheritor = psiClass.unwrapped as? KtClassOrObject ?: return@Processor true
|
||||
runReadAction {
|
||||
val inheritorDescriptor = inheritor.unsafeResolveToDescriptor() as ClassDescriptor
|
||||
val substitutor =
|
||||
getTypeSubstitutor(baseClassDescriptor.defaultType, inheritorDescriptor.defaultType) ?: return@runReadAction true
|
||||
baseDescriptors.forEach {
|
||||
val superMember = it.source.getPsi()!!
|
||||
val overridingDescriptor = (it.substitute(substitutor) as? CallableMemberDescriptor)?.let { memberDescriptor ->
|
||||
inheritorDescriptor.findCallableMemberBySignature(memberDescriptor)
|
||||
}
|
||||
val overridingMember = overridingDescriptor?.source?.getPsi()
|
||||
if (overridingMember != null) {
|
||||
if (!processor(superMember, overridingMember)) return@runReadAction false
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
})
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun KtNamedDeclaration.forEachOverridingElement(
|
||||
scope: SearchScope = runReadAction { useScope },
|
||||
processor: (superMember: PsiElement, overridingMember: PsiElement) -> Boolean
|
||||
): Boolean {
|
||||
val ktClass = runReadAction { containingClassOrObject as? KtClass } ?: return true
|
||||
|
||||
toLightMethods().forEach { baseMethod ->
|
||||
if (!OverridingMethodsSearch.search(baseMethod, scope.excludeKotlinSources(), true).all { processor(baseMethod, it) }) return false
|
||||
}
|
||||
|
||||
return forEachKotlinOverride(ktClass, listOf(this), scope) { baseElement, overrider -> processor(baseElement, overrider) }
|
||||
}
|
||||
|
||||
fun PsiMethod.forEachOverridingMethod(
|
||||
scope: SearchScope = runReadAction { useScope },
|
||||
processor: (PsiMethod) -> Boolean
|
||||
): Boolean {
|
||||
if (this !is KtFakeLightMethod) {
|
||||
if (!OverridingMethodsSearch.search(this, scope.excludeKotlinSources(), true).forEach(processor)) return false
|
||||
}
|
||||
|
||||
val ktMember = this.unwrapped as? KtNamedDeclaration ?: return true
|
||||
val ktClass = runReadAction { ktMember.containingClassOrObject as? KtClass } ?: return true
|
||||
return forEachKotlinOverride(ktClass, listOf(ktMember), scope) { _, overrider ->
|
||||
val lightMethods = runReadAction { overrider.toPossiblyFakeLightMethods().distinctBy { it.unwrapped } }
|
||||
lightMethods.all { processor(it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun PsiMethod.forEachImplementation(
|
||||
scope: SearchScope = runReadAction { useScope },
|
||||
processor: (PsiElement) -> Boolean
|
||||
): Boolean {
|
||||
return forEachOverridingMethod(scope, processor)
|
||||
&& FunctionalExpressionSearch.search(this, scope.excludeKotlinSources()).forEach(processor)
|
||||
}
|
||||
|
||||
fun PsiClass.forEachDeclaredMemberOverride(processor: (superMember: PsiElement, overridingMember: PsiElement) -> Boolean) {
|
||||
val scope = runReadAction { useScope }
|
||||
|
||||
if (this !is KtFakeLightClass) {
|
||||
AllOverridingMethodsSearch.search(this, scope.excludeKotlinSources()).all { processor(it.first, it.second) }
|
||||
}
|
||||
|
||||
val ktClass = unwrapped as? KtClass ?: return
|
||||
val members = ktClass.declarations.filterIsInstance<KtNamedDeclaration>() +
|
||||
ktClass.primaryConstructorParameters.filter { it.hasValOrVar() }
|
||||
forEachKotlinOverride(ktClass, members, scope, processor)
|
||||
}
|
||||
|
||||
fun findDeepestSuperMethodsNoWrapping(method: PsiElement): List<PsiElement> {
|
||||
val element = method.unwrapped
|
||||
return when (element) {
|
||||
is PsiMethod -> element.findDeepestSuperMethods().toList()
|
||||
is KtCallableDeclaration -> {
|
||||
val descriptor = element.resolveToDescriptorIfAny() as? CallableMemberDescriptor ?: return emptyList()
|
||||
descriptor.getDeepestSuperDeclarations(false).mapNotNull {
|
||||
it.source.getPsi() ?: DescriptorToSourceUtilsIde.getAnyDeclaration(element.project, it)
|
||||
}
|
||||
}
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
fun findDeepestSuperMethodsKotlinAware(method: PsiElement) =
|
||||
findDeepestSuperMethodsNoWrapping(method).mapNotNull { it.getRepresentativeLightMethod() }
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.search.ideaExtensions
|
||||
|
||||
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.psi.KtImportAlias
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
|
||||
|
||||
class KotlinImportAliasGotoDeclarationHandler : GotoDeclarationHandler {
|
||||
override fun getGotoDeclarationTargets(sourceElement: PsiElement?, offset: Int, editor: Editor?): Array<PsiElement>? {
|
||||
val importAlias = sourceElement?.parent as? KtImportAlias ?: return null
|
||||
|
||||
val result =
|
||||
runReadAction {
|
||||
importAlias.importDirective?.importedReference?.getQualifiedElementSelector()?.mainReference?.multiResolve(false)
|
||||
} ?: return null
|
||||
|
||||
return result.mapNotNull { it.element }.toTypedArray()
|
||||
}
|
||||
}
|
||||
-74
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* 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.search.ideaExtensions
|
||||
|
||||
import com.intellij.codeInsight.highlighting.JavaReadWriteAccessDetector
|
||||
import com.intellij.codeInsight.highlighting.ReadWriteAccessDetector
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiReference
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
|
||||
import org.jetbrains.kotlin.idea.references.ReferenceAccess
|
||||
import org.jetbrains.kotlin.idea.references.readWriteAccess
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
|
||||
class KotlinReadWriteAccessDetector : ReadWriteAccessDetector() {
|
||||
companion object {
|
||||
val INSTANCE = KotlinReadWriteAccessDetector()
|
||||
}
|
||||
|
||||
override fun isReadWriteAccessible(element: PsiElement) = element is KtVariableDeclaration || element is KtParameter
|
||||
|
||||
override fun isDeclarationWriteAccess(element: PsiElement) = isReadWriteAccessible(element)
|
||||
|
||||
override fun getReferenceAccess(referencedElement: PsiElement, reference: PsiReference): Access {
|
||||
if (!isReadWriteAccessible(referencedElement)) {
|
||||
return Access.Read
|
||||
}
|
||||
|
||||
val refTarget = reference.resolve()
|
||||
if (refTarget is KtLightMethod) {
|
||||
val origin = refTarget.kotlinOrigin
|
||||
val declaration: KtNamedDeclaration = when (origin) {
|
||||
is KtPropertyAccessor -> origin.getNonStrictParentOfType<KtProperty>()
|
||||
is KtProperty, is KtParameter -> origin as KtNamedDeclaration
|
||||
else -> null
|
||||
} ?: return Access.ReadWrite
|
||||
|
||||
return when (refTarget.name) {
|
||||
JvmAbi.getterName(declaration.name!!) -> return Access.Read
|
||||
JvmAbi.setterName(declaration.name!!) -> return Access.Write
|
||||
else -> Access.ReadWrite
|
||||
}
|
||||
}
|
||||
|
||||
return getExpressionAccess(reference.element)
|
||||
}
|
||||
|
||||
override fun getExpressionAccess(expression: PsiElement): Access {
|
||||
if (expression !is KtExpression) { //TODO: there should be a more correct scheme of access type detection for cross-language references
|
||||
return JavaReadWriteAccessDetector().getExpressionAccess(expression)
|
||||
}
|
||||
|
||||
return when (expression.readWriteAccess(useResolveForReadWrite = true)) {
|
||||
ReferenceAccess.READ -> Access.Read
|
||||
ReferenceAccess.WRITE -> Access.Write
|
||||
ReferenceAccess.READ_WRITE -> Access.ReadWrite
|
||||
}
|
||||
}
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.search.ideaExtensions
|
||||
|
||||
import com.intellij.ide.highlighter.JavaFileType
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.search.*
|
||||
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.search.excludeFileTypes
|
||||
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
class KotlinReferenceScopeOptimizer : ScopeOptimizer {
|
||||
override fun getRestrictedUseScope(element: PsiElement): SearchScope? {
|
||||
if (element is KtCallableDeclaration && element.parent is KtFile) {
|
||||
return getRestrictedScopeForTopLevelCallable(element)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getRestrictedScopeForTopLevelCallable(callable: KtCallableDeclaration): GlobalSearchScope? {
|
||||
val useScope = callable.useScope as? GlobalSearchScope ?: return null
|
||||
val file = callable.parent as KtFile
|
||||
val packageName = file.packageFqName.takeUnless { it.isRoot } ?: return null
|
||||
val project = file.project
|
||||
val searchHelper = PsiSearchHelper.getInstance(project)
|
||||
|
||||
val kotlinScope = GlobalSearchScope.getScopeRestrictedByFileTypes(useScope, KotlinFileType.INSTANCE)
|
||||
val javaScope = GlobalSearchScope.getScopeRestrictedByFileTypes(useScope, JavaFileType.INSTANCE)
|
||||
val restScope = useScope.excludeFileTypes(KotlinFileType.INSTANCE, JavaFileType.INSTANCE) as GlobalSearchScope
|
||||
|
||||
val kotlinFiles = mutableListOf<VirtualFile>()
|
||||
searchHelper.processCandidateFilesForText(kotlinScope, UsageSearchContext.IN_CODE, true, packageName.asString()) {
|
||||
kotlinFiles.add(it)
|
||||
}
|
||||
|
||||
val javaFiles = mutableListOf<VirtualFile>()
|
||||
searchHelper.processCandidateFilesForText(javaScope, UsageSearchContext.IN_CODE, true, file.javaFileFacadeFqName.asString()) {
|
||||
javaFiles.add(it)
|
||||
}
|
||||
|
||||
return GlobalSearchScope.filesScope(project, kotlinFiles + javaFiles).uniteWith(restScope)
|
||||
}
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.search.ideaExtensions
|
||||
|
||||
import com.intellij.ide.highlighter.JavaFileType
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.impl.cache.CacheManager
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.search.ScopeOptimizer
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.psi.search.UsageSearchContext
|
||||
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.search.excludeFileTypes
|
||||
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
class KotlinReferenceScopeOptimizer : ScopeOptimizer {
|
||||
override fun getRestrictedUseScope(element: PsiElement): SearchScope? {
|
||||
if (element is KtCallableDeclaration && element.parent is KtFile) {
|
||||
return getRestrictedScopeForTopLevelCallable(element)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getRestrictedScopeForTopLevelCallable(callable: KtCallableDeclaration): GlobalSearchScope? {
|
||||
val useScope = callable.useScope as? GlobalSearchScope ?: return null
|
||||
val file = callable.parent as KtFile
|
||||
val packageName = file.packageFqName.takeUnless { it.isRoot } ?: return null
|
||||
val project = file.project
|
||||
val cacheManager = CacheManager.SERVICE.getInstance(project)
|
||||
|
||||
val kotlinScope = GlobalSearchScope.getScopeRestrictedByFileTypes(useScope, KotlinFileType.INSTANCE)
|
||||
val javaScope = GlobalSearchScope.getScopeRestrictedByFileTypes(useScope, JavaFileType.INSTANCE)
|
||||
val restScope = useScope.excludeFileTypes(KotlinFileType.INSTANCE, JavaFileType.INSTANCE) as GlobalSearchScope
|
||||
|
||||
//TODO: use all components of package name?
|
||||
val shortPackageName = packageName.shortName().identifier
|
||||
val kotlinFiles = cacheManager.getVirtualFilesWithWord(shortPackageName, UsageSearchContext.IN_CODE, kotlinScope, true)
|
||||
|
||||
val javaFacadeName = file.javaFileFacadeFqName.shortName().identifier
|
||||
val javaFiles = cacheManager.getVirtualFilesWithWord(javaFacadeName, UsageSearchContext.IN_CODE, javaScope, true)
|
||||
|
||||
return GlobalSearchScope.filesScope(project, (kotlinFiles + javaFiles).asList()).uniteWith(restScope)
|
||||
}
|
||||
}
|
||||
-408
@@ -1,408 +0,0 @@
|
||||
/*
|
||||
* 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.search.ideaExtensions
|
||||
|
||||
import com.intellij.openapi.application.QueryExecutorBase
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.cache.CacheManager
|
||||
import com.intellij.psi.search.*
|
||||
import com.intellij.psi.search.searches.MethodReferencesSearch
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import com.intellij.util.Processor
|
||||
import com.intellij.util.containers.nullize
|
||||
import org.jetbrains.kotlin.asJava.LightClassUtil
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightField
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightMember
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightParameter
|
||||
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
||||
import org.jetbrains.kotlin.asJava.toLightClass
|
||||
import org.jetbrains.kotlin.asJava.toLightElements
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.search.*
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions.Companion.Empty
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions.Companion.calculateEffectiveScope
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.dataClassComponentFunction
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.filterDataClassComponentsIfDisabled
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.getClassNameForCompanionObject
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.operators.OperatorReferenceSearcher
|
||||
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.idea.util.expectedDeclarationIfAny
|
||||
import org.jetbrains.kotlin.idea.util.isExpectDeclaration
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parents
|
||||
import java.util.*
|
||||
|
||||
data class KotlinReferencesSearchOptions(
|
||||
val acceptCallableOverrides: Boolean = false,
|
||||
val acceptOverloads: Boolean = false,
|
||||
val acceptExtensionsOfDeclarationClass: Boolean = false,
|
||||
val acceptCompanionObjectMembers: Boolean = false,
|
||||
val acceptImportAlias: Boolean = true,
|
||||
val searchForComponentConventions: Boolean = true,
|
||||
val searchForOperatorConventions: Boolean = true,
|
||||
val searchNamedArguments: Boolean = true,
|
||||
val searchForExpectedUsages: Boolean = true
|
||||
) {
|
||||
fun anyEnabled(): Boolean = acceptCallableOverrides || acceptOverloads || acceptExtensionsOfDeclarationClass
|
||||
|
||||
companion object {
|
||||
val Empty = KotlinReferencesSearchOptions()
|
||||
|
||||
internal fun calculateEffectiveScope(
|
||||
elementToSearch: PsiNamedElement,
|
||||
parameters: ReferencesSearch.SearchParameters
|
||||
): SearchScope {
|
||||
val kotlinOptions = (parameters as? KotlinAwareReferencesSearchParameters)?.kotlinOptions ?: Empty
|
||||
val elements = if (elementToSearch is KtDeclaration && !isOnlyKotlinSearch(parameters.scopeDeterminedByUser)) {
|
||||
elementToSearch.toLightElements().filterDataClassComponentsIfDisabled(kotlinOptions).nullize()
|
||||
} else {
|
||||
null
|
||||
} ?: listOf(elementToSearch)
|
||||
|
||||
return elements.fold(parameters.effectiveSearchScope) { scope, e ->
|
||||
scope.unionSafe(parameters.effectiveSearchScope(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface KotlinAwareReferencesSearchParameters {
|
||||
val kotlinOptions: KotlinReferencesSearchOptions
|
||||
}
|
||||
|
||||
class KotlinReferencesSearchParameters(
|
||||
elementToSearch: PsiElement,
|
||||
scope: SearchScope = runReadAction { elementToSearch.project.allScope() },
|
||||
ignoreAccessScope: Boolean = false,
|
||||
optimizer: SearchRequestCollector? = null,
|
||||
override val kotlinOptions: KotlinReferencesSearchOptions = Empty
|
||||
) : ReferencesSearch.SearchParameters(elementToSearch, scope, ignoreAccessScope, optimizer), KotlinAwareReferencesSearchParameters
|
||||
|
||||
class KotlinMethodReferencesSearchParameters(
|
||||
elementToSearch: PsiMethod,
|
||||
scope: SearchScope = runReadAction { elementToSearch.project.allScope() },
|
||||
strictSignatureSearch: Boolean = true,
|
||||
override val kotlinOptions: KotlinReferencesSearchOptions = Empty
|
||||
) : MethodReferencesSearch.SearchParameters(elementToSearch, scope, strictSignatureSearch), KotlinAwareReferencesSearchParameters
|
||||
|
||||
class KotlinAliasedImportedElementSearcher : QueryExecutorBase<PsiReference, ReferencesSearch.SearchParameters>(true) {
|
||||
override fun processQuery(parameters: ReferencesSearch.SearchParameters, consumer: Processor<in PsiReference?>) {
|
||||
val kotlinOptions = (parameters as? KotlinAwareReferencesSearchParameters)?.kotlinOptions ?: Empty
|
||||
if (!kotlinOptions.acceptImportAlias) return
|
||||
val element = parameters.elementToSearch
|
||||
if (!element.isValid) return
|
||||
val unwrappedElement = element.namedUnwrappedElement ?: return
|
||||
val name = unwrappedElement.name
|
||||
if (name == null || StringUtil.isEmptyOrSpaces(name)) return
|
||||
val effectiveSearchScope = calculateEffectiveScope(unwrappedElement, parameters)
|
||||
|
||||
val collector = parameters.optimizer
|
||||
val session = collector.searchSession
|
||||
collector.searchWord(name, effectiveSearchScope, UsageSearchContext.IN_CODE, true, element, AliasProcessor(element, session))
|
||||
}
|
||||
|
||||
private class AliasProcessor(
|
||||
private val myTarget: PsiElement,
|
||||
private val mySession: SearchSession
|
||||
) : RequestResultProcessor(myTarget) {
|
||||
override fun processTextOccurrence(element: PsiElement, offsetInElement: Int, consumer: Processor<in PsiReference>): Boolean {
|
||||
val importStatement = element.parent as? KtImportDirective ?: return true
|
||||
val importAlias = importStatement.alias?.name ?: return true
|
||||
|
||||
val reference = importStatement.importedReference?.getQualifiedElementSelector()?.mainReference ?: return true
|
||||
if (!reference.isReferenceTo(myTarget)) {
|
||||
return true
|
||||
}
|
||||
|
||||
val collector = SearchRequestCollector(mySession)
|
||||
val fileScope: SearchScope = LocalSearchScope(element.containingFile)
|
||||
collector.searchWord(importAlias, fileScope, UsageSearchContext.IN_CODE, true, myTarget)
|
||||
return PsiSearchHelper.getInstance(element.project).processRequests(collector, consumer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, ReferencesSearch.SearchParameters>() {
|
||||
|
||||
override fun processQuery(queryParameters: ReferencesSearch.SearchParameters, consumer: Processor<in PsiReference>) {
|
||||
val processor = QueryProcessor(queryParameters, consumer)
|
||||
runReadAction { processor.processInReadAction() }
|
||||
processor.executeLongRunningTasks()
|
||||
}
|
||||
|
||||
private class QueryProcessor(val queryParameters: ReferencesSearch.SearchParameters, val consumer: Processor<in PsiReference>) {
|
||||
|
||||
private val kotlinOptions = (queryParameters as? KotlinAwareReferencesSearchParameters)?.kotlinOptions ?: Empty
|
||||
|
||||
private val longTasks = ArrayList<() -> Unit>()
|
||||
|
||||
fun executeLongRunningTasks() {
|
||||
longTasks.forEach { it() }
|
||||
}
|
||||
|
||||
fun processInReadAction() {
|
||||
val element = queryParameters.elementToSearch
|
||||
if (!element.isValid) return
|
||||
|
||||
val unwrappedElement = element.namedUnwrappedElement ?: return
|
||||
|
||||
val elementToSearch =
|
||||
if (kotlinOptions.searchForExpectedUsages && unwrappedElement is KtDeclaration && unwrappedElement.hasActualModifier()) {
|
||||
unwrappedElement.expectedDeclarationIfAny() as? PsiNamedElement
|
||||
} else {
|
||||
null
|
||||
} ?: unwrappedElement
|
||||
|
||||
val effectiveSearchScope = calculateEffectiveScope(elementToSearch, queryParameters)
|
||||
|
||||
val refFilter: (PsiReference) -> Boolean = when (elementToSearch) {
|
||||
is KtParameter -> ({ ref: PsiReference -> !ref.isNamedArgumentReference()/* they are processed later*/ })
|
||||
else -> ({ true })
|
||||
}
|
||||
|
||||
val resultProcessor = KotlinRequestResultProcessor(elementToSearch, filter = refFilter, options = kotlinOptions)
|
||||
|
||||
val name = elementToSearch.name
|
||||
if (kotlinOptions.anyEnabled() || elementToSearch is KtNamedDeclaration && elementToSearch.isExpectDeclaration()) {
|
||||
if (name != null) {
|
||||
// Check difference with default scope
|
||||
queryParameters.optimizer.searchWord(
|
||||
name, effectiveSearchScope, UsageSearchContext.IN_CODE, true, elementToSearch, resultProcessor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val classNameForCompanionObject = elementToSearch.getClassNameForCompanionObject()
|
||||
if (classNameForCompanionObject != null) {
|
||||
queryParameters.optimizer.searchWord(
|
||||
classNameForCompanionObject, effectiveSearchScope, UsageSearchContext.ANY, true, elementToSearch, resultProcessor
|
||||
)
|
||||
}
|
||||
|
||||
if (elementToSearch is KtParameter && kotlinOptions.searchNamedArguments) {
|
||||
searchNamedArguments(elementToSearch)
|
||||
}
|
||||
|
||||
if (!(elementToSearch is KtElement && isOnlyKotlinSearch(effectiveSearchScope))) {
|
||||
searchLightElements(element)
|
||||
}
|
||||
|
||||
if (element is KtFunction || element is PsiMethod) {
|
||||
val referenceSearcher = OperatorReferenceSearcher.create(
|
||||
element, effectiveSearchScope, consumer, queryParameters.optimizer, kotlinOptions
|
||||
)
|
||||
if (referenceSearcher != null) {
|
||||
longTasks.add { referenceSearcher.run() }
|
||||
}
|
||||
}
|
||||
|
||||
if (kotlinOptions.searchForComponentConventions) {
|
||||
when (element) {
|
||||
is KtParameter -> {
|
||||
val componentFunctionDescriptor = element.dataClassComponentFunction()
|
||||
if (componentFunctionDescriptor != null) {
|
||||
val containingClass = element.getStrictParentOfType<KtClassOrObject>()?.toLightClass()
|
||||
searchDataClassComponentUsages(containingClass, componentFunctionDescriptor, kotlinOptions)
|
||||
}
|
||||
}
|
||||
|
||||
is KtLightParameter -> {
|
||||
val componentFunctionDescriptor = element.kotlinOrigin?.dataClassComponentFunction()
|
||||
if (componentFunctionDescriptor != null) {
|
||||
searchDataClassComponentUsages(element.method.containingClass, componentFunctionDescriptor, kotlinOptions)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun searchNamedArguments(parameter: KtParameter) {
|
||||
val parameterName = parameter.name ?: return
|
||||
val function = parameter.ownerFunction as? KtFunction ?: return
|
||||
if (function.nameAsName?.isSpecial != false) return
|
||||
val project = function.project
|
||||
var namedArgsScope = function.useScope.intersectWith(queryParameters.scopeDeterminedByUser)
|
||||
|
||||
if (namedArgsScope is GlobalSearchScope) {
|
||||
namedArgsScope = KotlinSourceFilterScope.sourcesAndLibraries(namedArgsScope, project)
|
||||
|
||||
val filesWithFunctionName = CacheManager.SERVICE.getInstance(project).getVirtualFilesWithWord(
|
||||
function.name!!, UsageSearchContext.IN_CODE, namedArgsScope, true
|
||||
)
|
||||
namedArgsScope = GlobalSearchScope.filesScope(project, filesWithFunctionName.asList())
|
||||
}
|
||||
|
||||
val processor = KotlinRequestResultProcessor(parameter, filter = { it.isNamedArgumentReference() })
|
||||
queryParameters.optimizer.searchWord(
|
||||
parameterName,
|
||||
namedArgsScope,
|
||||
KOTLIN_NAMED_ARGUMENT_SEARCH_CONTEXT,
|
||||
true,
|
||||
parameter,
|
||||
processor
|
||||
)
|
||||
}
|
||||
|
||||
private fun searchLightElements(element: PsiElement) {
|
||||
when (element) {
|
||||
is KtClassOrObject -> {
|
||||
processKtClassOrObject(element)
|
||||
}
|
||||
|
||||
is KtNamedFunction, is KtSecondaryConstructor -> {
|
||||
val name = (element as KtFunction).name
|
||||
if (name != null) {
|
||||
val methods = LightClassUtil.getLightClassMethods(element)
|
||||
for (method in methods) {
|
||||
searchNamedElement(method)
|
||||
}
|
||||
}
|
||||
|
||||
processStaticsFromCompanionObject(element)
|
||||
}
|
||||
|
||||
is KtProperty -> {
|
||||
val propertyMethods = LightClassUtil.getLightClassPropertyMethods(element)
|
||||
propertyMethods.allDeclarations.forEach { searchNamedElement(it) }
|
||||
processStaticsFromCompanionObject(element)
|
||||
}
|
||||
|
||||
is KtParameter -> {
|
||||
searchPropertyAccessorMethods(element)
|
||||
if (element.getStrictParentOfType<KtPrimaryConstructor>() != null) {
|
||||
// Simple parameters without val and var shouldn't be processed here because of local search scope
|
||||
val methods = LightClassUtil.getLightClassPropertyMethods(element)
|
||||
methods.allDeclarations.filterDataClassComponentsIfDisabled(kotlinOptions).forEach { searchNamedElement(it) }
|
||||
}
|
||||
}
|
||||
|
||||
is KtLightMethod -> {
|
||||
val declaration = element.kotlinOrigin
|
||||
if (declaration is KtProperty || (declaration is KtParameter && declaration.hasValOrVar())) {
|
||||
searchNamedElement(declaration as PsiNamedElement)
|
||||
processStaticsFromCompanionObject(declaration)
|
||||
} else if (declaration is KtPropertyAccessor) {
|
||||
val property = declaration.getStrictParentOfType<KtProperty>()
|
||||
searchNamedElement(property)
|
||||
} else if (declaration is KtFunction) {
|
||||
processStaticsFromCompanionObject(declaration)
|
||||
if (element.isMangled) {
|
||||
searchNamedElement(declaration) { it.restrictToKotlinSources() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is KtLightParameter -> {
|
||||
val origin = element.kotlinOrigin ?: return
|
||||
searchPropertyAccessorMethods(origin)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun searchPropertyAccessorMethods(origin: KtParameter) {
|
||||
origin.toLightElements().filterDataClassComponentsIfDisabled(kotlinOptions).forEach { searchNamedElement(it) }
|
||||
}
|
||||
|
||||
private fun processKtClassOrObject(element: KtClassOrObject) {
|
||||
val className = element.name ?: return
|
||||
val lightClass = element.toLightClass() ?: return
|
||||
searchNamedElement(lightClass, className)
|
||||
|
||||
if (element is KtObjectDeclaration && element.isCompanion()) {
|
||||
LightClassUtil.getLightFieldForCompanionObject(element)?.let { searchNamedElement(it) }
|
||||
|
||||
if (kotlinOptions.acceptCompanionObjectMembers) {
|
||||
val originLightClass = element.getStrictParentOfType<KtClass>()?.toLightClass()
|
||||
if (originLightClass != null) {
|
||||
val lightDeclarations: List<KtLightMember<*>?> =
|
||||
originLightClass.methods.map { it as? KtLightMethod } + originLightClass.fields.map { it as? KtLightField }
|
||||
|
||||
for (declaration in element.declarations) {
|
||||
lightDeclarations
|
||||
.firstOrNull { it?.kotlinOrigin == declaration }
|
||||
?.let { searchNamedElement(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun searchDataClassComponentUsages(
|
||||
containingClass: PsiClass?,
|
||||
componentFunctionDescriptor: FunctionDescriptor,
|
||||
kotlinOptions: KotlinReferencesSearchOptions
|
||||
) {
|
||||
val componentFunction = containingClass?.methods?.firstOrNull {
|
||||
it.name == componentFunctionDescriptor.name.asString() && it.parameterList.parametersCount == 0
|
||||
}
|
||||
if (componentFunction != null) {
|
||||
searchNamedElement(componentFunction)
|
||||
|
||||
val searcher = OperatorReferenceSearcher.create(
|
||||
componentFunction, queryParameters.effectiveSearchScope, consumer, queryParameters.optimizer, kotlinOptions
|
||||
)
|
||||
longTasks.add { searcher!!.run() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun processStaticsFromCompanionObject(element: KtDeclaration) {
|
||||
findStaticMethodsFromCompanionObject(element).forEach { searchNamedElement(it) }
|
||||
}
|
||||
|
||||
private fun findStaticMethodsFromCompanionObject(declaration: KtDeclaration): List<PsiMethod> {
|
||||
val originObject = declaration.parents
|
||||
.dropWhile { it is KtClassBody }
|
||||
.firstOrNull() as? KtObjectDeclaration ?: return emptyList()
|
||||
if (!originObject.isCompanion()) return emptyList()
|
||||
val originClass = originObject.getStrictParentOfType<KtClass>()
|
||||
val originLightClass = originClass?.toLightClass() ?: return emptyList()
|
||||
val allMethods = originLightClass.allMethods
|
||||
return allMethods.filter { it is KtLightMethod && it.kotlinOrigin == declaration }
|
||||
}
|
||||
|
||||
private fun searchNamedElement(
|
||||
element: PsiNamedElement?,
|
||||
name: String? = element?.name,
|
||||
modifyScope: ((SearchScope) -> SearchScope)? = null
|
||||
) {
|
||||
if (name != null && element != null) {
|
||||
val baseScope = queryParameters.effectiveSearchScope(element)
|
||||
val scope = if (modifyScope != null) modifyScope(baseScope) else baseScope
|
||||
val context = UsageSearchContext.IN_CODE + UsageSearchContext.IN_FOREIGN_LANGUAGES + UsageSearchContext.IN_COMMENTS
|
||||
val resultProcessor = KotlinRequestResultProcessor(
|
||||
element,
|
||||
queryParameters.elementToSearch.namedUnwrappedElement ?: element,
|
||||
options = kotlinOptions
|
||||
)
|
||||
queryParameters.optimizer.searchWord(name, scope, context.toShort(), true, element, resultProcessor)
|
||||
}
|
||||
}
|
||||
|
||||
private fun PsiReference.isNamedArgumentReference(): Boolean {
|
||||
return this is KtSimpleNameReference && expression.parent is KtValueArgumentName
|
||||
}
|
||||
}
|
||||
}
|
||||
-79
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* 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.search.ideaExtensions
|
||||
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.psi.PsiReferenceService
|
||||
import com.intellij.psi.ReferenceRange
|
||||
import com.intellij.psi.search.RequestResultProcessor
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.isCallableOverrideUsage
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.isExtensionOfDeclarationClassUsage
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.isUsageInContainingDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtDestructuringDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||
|
||||
class KotlinRequestResultProcessor(
|
||||
private val unwrappedElement: PsiElement,
|
||||
private val originalElement: PsiElement = unwrappedElement,
|
||||
private val filter: (PsiReference) -> Boolean = { true },
|
||||
private val options: KotlinReferencesSearchOptions = KotlinReferencesSearchOptions.Empty
|
||||
) : RequestResultProcessor(unwrappedElement, originalElement, filter, options) {
|
||||
private val referenceService = PsiReferenceService.getService()
|
||||
|
||||
override fun processTextOccurrence(element: PsiElement, offsetInElement: Int, consumer: Processor<in PsiReference>): Boolean {
|
||||
val references = if (element is KtDestructuringDeclaration)
|
||||
element.entries.flatMap { referenceService.getReferences(it, PsiReferenceService.Hints.NO_HINTS) }
|
||||
else
|
||||
referenceService.getReferences(element, PsiReferenceService.Hints.NO_HINTS)
|
||||
return references.all { ref ->
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
if (filter(ref) && ref.containsOffsetInElement(offsetInElement) && ref.isReferenceToTarget(unwrappedElement)) {
|
||||
consumer.process(ref)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun PsiReference.containsOffsetInElement(offsetInElement: Int): Boolean {
|
||||
if (this is KtDestructuringDeclarationReference) return true
|
||||
return ReferenceRange.containsOffsetInElement(this, offsetInElement)
|
||||
}
|
||||
|
||||
private fun PsiReference.isReferenceToTarget(element: PsiElement): Boolean {
|
||||
if (isReferenceTo(element)) {
|
||||
return true
|
||||
}
|
||||
if (originalElement is KtNamedDeclaration) {
|
||||
if (options.acceptCallableOverrides && isCallableOverrideUsage(originalElement)) {
|
||||
return true
|
||||
}
|
||||
if (options.acceptOverloads && isUsageInContainingDeclaration(originalElement)) {
|
||||
return true
|
||||
}
|
||||
if (options.acceptExtensionsOfDeclarationClass && isExtensionOfDeclarationClassUsage(originalElement)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -1,146 +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
|
||||
|
||||
import com.intellij.openapi.fileTypes.FileType
|
||||
import com.intellij.openapi.fileTypes.FileTypeRegistry
|
||||
import com.intellij.openapi.progress.ProgressIndicator
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.impl.cache.impl.id.IdIndex
|
||||
import com.intellij.psi.impl.cache.impl.id.IdIndexEntry
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.search.LocalSearchScope
|
||||
import com.intellij.psi.search.PsiSearchHelper
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import com.intellij.util.CommonProcessors
|
||||
import com.intellij.util.indexing.FileBasedIndex
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
import java.util.*
|
||||
|
||||
infix fun SearchScope.and(otherScope: SearchScope): SearchScope = intersectWith(otherScope)
|
||||
infix fun SearchScope.or(otherScope: SearchScope): SearchScope = union(otherScope)
|
||||
infix fun GlobalSearchScope.or(otherScope: SearchScope): GlobalSearchScope = union(otherScope)
|
||||
operator fun SearchScope.minus(otherScope: GlobalSearchScope): SearchScope = this and !otherScope
|
||||
operator fun GlobalSearchScope.not(): GlobalSearchScope = GlobalSearchScope.notScope(this)
|
||||
|
||||
fun SearchScope.unionSafe(other: SearchScope): SearchScope {
|
||||
if (this is LocalSearchScope && this.scope.isEmpty()) {
|
||||
return other
|
||||
}
|
||||
if (other is LocalSearchScope && other.scope.isEmpty()) {
|
||||
return this
|
||||
}
|
||||
return this.union(other)
|
||||
}
|
||||
|
||||
fun Project.allScope(): GlobalSearchScope = GlobalSearchScope.allScope(this)
|
||||
|
||||
fun Project.projectScope(): GlobalSearchScope = GlobalSearchScope.projectScope(this)
|
||||
|
||||
fun PsiFile.fileScope(): GlobalSearchScope = GlobalSearchScope.fileScope(this)
|
||||
|
||||
fun GlobalSearchScope.restrictByFileType(fileType: FileType) = GlobalSearchScope.getScopeRestrictedByFileTypes(this, fileType)
|
||||
|
||||
fun SearchScope.restrictByFileType(fileType: FileType): SearchScope = when (this) {
|
||||
is GlobalSearchScope -> restrictByFileType(fileType)
|
||||
is LocalSearchScope -> {
|
||||
val elements = scope.filter { it.containingFile.fileType == fileType }
|
||||
when (elements.size) {
|
||||
0 -> GlobalSearchScope.EMPTY_SCOPE
|
||||
scope.size -> this
|
||||
else -> LocalSearchScope(elements.toTypedArray())
|
||||
}
|
||||
}
|
||||
else -> this
|
||||
}
|
||||
|
||||
fun GlobalSearchScope.restrictToKotlinSources() = restrictByFileType(KotlinFileType.INSTANCE)
|
||||
|
||||
fun SearchScope.restrictToKotlinSources() = restrictByFileType(KotlinFileType.INSTANCE)
|
||||
|
||||
fun SearchScope.excludeKotlinSources(): SearchScope = excludeFileTypes(KotlinFileType.INSTANCE)
|
||||
|
||||
fun SearchScope.excludeFileTypes(vararg fileTypes: FileType): SearchScope {
|
||||
return if (this is GlobalSearchScope) {
|
||||
val includedFileTypes = FileTypeRegistry.getInstance().registeredFileTypes.filter { it !in fileTypes }.toTypedArray()
|
||||
GlobalSearchScope.getScopeRestrictedByFileTypes(this, *includedFileTypes)
|
||||
} else {
|
||||
this as LocalSearchScope
|
||||
val filteredElements = scope.filter { it.containingFile.fileType !in fileTypes }
|
||||
if (filteredElements.isNotEmpty())
|
||||
LocalSearchScope(filteredElements.toTypedArray())
|
||||
else
|
||||
GlobalSearchScope.EMPTY_SCOPE
|
||||
}
|
||||
}
|
||||
|
||||
// Copied from SearchParameters.getEffectiveSearchScope()
|
||||
fun ReferencesSearch.SearchParameters.effectiveSearchScope(element: PsiElement): SearchScope {
|
||||
if (element == elementToSearch) return effectiveSearchScope
|
||||
if (isIgnoreAccessScope) return scopeDeterminedByUser
|
||||
val accessScope = PsiSearchHelper.getInstance(element.project).getUseScope(element)
|
||||
return scopeDeterminedByUser.intersectWith(accessScope)
|
||||
}
|
||||
|
||||
fun isOnlyKotlinSearch(searchScope: SearchScope): Boolean {
|
||||
return searchScope is LocalSearchScope && searchScope.scope.all { it.containingFile is KtFile }
|
||||
}
|
||||
|
||||
fun PsiSearchHelper.isCheapEnoughToSearchConsideringOperators(
|
||||
name: String,
|
||||
scope: GlobalSearchScope,
|
||||
fileToIgnoreOccurrencesIn: PsiFile?,
|
||||
progress: ProgressIndicator?
|
||||
): PsiSearchHelper.SearchCostResult {
|
||||
if (OperatorConventions.isConventionName(Name.identifier(name))) {
|
||||
return PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES
|
||||
}
|
||||
|
||||
return isCheapEnoughToSearch(name, scope, fileToIgnoreOccurrencesIn, progress)
|
||||
}
|
||||
|
||||
fun findScriptsWithUsages(declaration: KtNamedDeclaration): List<KtFile> {
|
||||
val project = declaration.project
|
||||
val scope = PsiSearchHelper.getInstance(project).getUseScope(declaration) as? GlobalSearchScope
|
||||
?: return emptyList()
|
||||
|
||||
val name = declaration.name.takeIf { it?.isNotBlank() == true } ?: return emptyList()
|
||||
val collector = CommonProcessors.CollectProcessor(ArrayList<VirtualFile>())
|
||||
runReadAction {
|
||||
FileBasedIndex.getInstance().getFilesWithKey(
|
||||
IdIndex.NAME,
|
||||
setOf(IdIndexEntry(name, true)),
|
||||
collector,
|
||||
scope
|
||||
)
|
||||
}
|
||||
return collector.results
|
||||
.mapNotNull { PsiManager.getInstance(project).findFile(it) as? KtFile }
|
||||
.filter { it.findScriptDefinition() != null }
|
||||
.toList()
|
||||
}
|
||||
-940
@@ -1,940 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.search.usagesSearch
|
||||
|
||||
import com.intellij.ide.highlighter.JavaFileType
|
||||
import com.intellij.ide.highlighter.XmlFileType
|
||||
import com.intellij.lang.java.JavaLanguage
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.progress.ProcessCanceledException
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference
|
||||
import com.intellij.psi.search.*
|
||||
import com.intellij.psi.search.searches.ClassInheritorsSearch
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
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.toLightClass
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.PsiDiagnosticUtils
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.util.getJavaMemberDescriptor
|
||||
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
|
||||
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
|
||||
import org.jetbrains.kotlin.idea.search.excludeFileTypes
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchParameters
|
||||
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
|
||||
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.load.java.descriptors.JavaClassDescriptor
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.resolve.sam.getSingleAbstractMethodOrNull
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import java.util.*
|
||||
|
||||
//TODO: check if smart search is too expensive
|
||||
|
||||
class ExpressionsOfTypeProcessor(
|
||||
private val typeToSearch: FuzzyType,
|
||||
private val classToSearch: PsiClass?,
|
||||
private val searchScope: SearchScope,
|
||||
private val project: Project,
|
||||
private val possibleMatchHandler: (KtExpression) -> Unit,
|
||||
private val possibleMatchesInScopeHandler: (SearchScope) -> Unit
|
||||
) {
|
||||
/** For tests only */
|
||||
enum class Mode {
|
||||
ALWAYS_SMART,
|
||||
ALWAYS_PLAIN,
|
||||
PLAIN_WHEN_NEEDED // use plain search for LocalSearchScope and when unknown type of reference encountered
|
||||
}
|
||||
|
||||
companion object {
|
||||
@get:TestOnly
|
||||
var mode = if (ApplicationManager.getApplication().isUnitTestMode) Mode.ALWAYS_SMART else Mode.PLAIN_WHEN_NEEDED
|
||||
|
||||
@get:TestOnly
|
||||
var testLog: MutableCollection<String>? = null
|
||||
|
||||
inline fun testLog(s: () -> String) {
|
||||
testLog?.add(s())
|
||||
}
|
||||
|
||||
val LOG = Logger.getInstance(ExpressionsOfTypeProcessor::class.java)
|
||||
|
||||
fun logPresentation(element: PsiElement): String? {
|
||||
return runReadAction {
|
||||
if (element !is KtDeclaration && element !is PsiMember) return@runReadAction element.text
|
||||
val fqName = element.getKotlinFqName()?.asString()
|
||||
?: (element as? KtNamedDeclaration)?.name
|
||||
when (element) {
|
||||
is PsiMethod -> fqName + element.parameterList.text
|
||||
is KtFunction -> fqName + element.valueParameterList!!.text
|
||||
is KtParameter -> {
|
||||
val owner = element.ownerFunction?.let { logPresentation(it) } ?: element.parent.toString()
|
||||
"parameter ${element.name} of $owner"
|
||||
}
|
||||
is KtDestructuringDeclaration -> element.entries.joinToString(", ", prefix = "(", postfix = ")") { it.text }
|
||||
else -> fqName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun PsiModifierListOwner.isPrivate() = hasModifierProperty(PsiModifier.PRIVATE)
|
||||
|
||||
private fun PsiModifierListOwner.isLocal() = parents.any { it is PsiCodeBlock }
|
||||
}
|
||||
|
||||
// note: a Task must define equals & hashCode!
|
||||
private interface Task {
|
||||
fun perform()
|
||||
}
|
||||
|
||||
private val tasks = ArrayDeque<Task>()
|
||||
private val taskSet = HashSet<Task>()
|
||||
|
||||
private val scopesToUsePlainSearch = LinkedHashMap<KtFile, ArrayList<PsiElement>>()
|
||||
|
||||
fun run() {
|
||||
val usePlainSearch = when (mode) {
|
||||
Mode.ALWAYS_SMART -> false
|
||||
Mode.ALWAYS_PLAIN -> true
|
||||
Mode.PLAIN_WHEN_NEEDED -> searchScope is LocalSearchScope // for local scope it's faster to use plain search
|
||||
}
|
||||
if (usePlainSearch || classToSearch == null) {
|
||||
possibleMatchesInScopeHandler(searchScope)
|
||||
return
|
||||
}
|
||||
|
||||
// optimization
|
||||
if (runReadAction {
|
||||
searchScope is GlobalSearchScope && !FileTypeIndex.containsFileOfType(
|
||||
KotlinFileType.INSTANCE,
|
||||
searchScope
|
||||
)
|
||||
}) return
|
||||
|
||||
// for class from library always use plain search because we cannot search usages in compiled code (we could though)
|
||||
if (!runReadAction { classToSearch.isValid && ProjectRootsUtil.isInProjectSource(classToSearch) }) {
|
||||
possibleMatchesInScopeHandler(searchScope)
|
||||
return
|
||||
}
|
||||
|
||||
addClassToProcess(classToSearch)
|
||||
|
||||
processTasks()
|
||||
|
||||
runReadAction {
|
||||
val scopeElements = scopesToUsePlainSearch.values
|
||||
.flatten()
|
||||
.filter { it.isValid }
|
||||
.toTypedArray()
|
||||
if (scopeElements.isNotEmpty()) {
|
||||
possibleMatchesInScopeHandler(LocalSearchScope(scopeElements))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addTask(task: Task) {
|
||||
if (taskSet.add(task)) {
|
||||
tasks.push(task)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun processTasks() {
|
||||
while (tasks.isNotEmpty()) {
|
||||
tasks.pop().perform()
|
||||
}
|
||||
}
|
||||
|
||||
private fun downShiftToPlainSearch(reference: PsiReference) {
|
||||
val message = getFallbackDiagnosticsMessage(reference)
|
||||
LOG.info("ExpressionsOfTypeProcessor: $message")
|
||||
testLog { "Downgrade to plain text search: $message" }
|
||||
|
||||
tasks.clear()
|
||||
scopesToUsePlainSearch.clear()
|
||||
possibleMatchesInScopeHandler(searchScope)
|
||||
}
|
||||
|
||||
private fun checkPsiClass(psiClass: PsiClass): Boolean {
|
||||
// we don't filter out private classes because we can inherit public class from private inside the same visibility scope
|
||||
if (psiClass.isLocal()) {
|
||||
return false
|
||||
}
|
||||
|
||||
val qualifiedName = runReadAction { psiClass.qualifiedName }
|
||||
if (qualifiedName == null || qualifiedName.isEmpty()) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun addNonKotlinClassToProcess(classToSearch: PsiClass) {
|
||||
if (!checkPsiClass(classToSearch)) {
|
||||
return
|
||||
}
|
||||
|
||||
addClassToProcess(classToSearch)
|
||||
}
|
||||
|
||||
private fun addClassToProcess(classToSearch: PsiClass) {
|
||||
data class ProcessClassUsagesTask(val classToSearch: PsiClass) : Task {
|
||||
override fun perform() {
|
||||
testLog { "Searched references to ${logPresentation(classToSearch)}" }
|
||||
val scope = GlobalSearchScope.allScope(project)
|
||||
.excludeFileTypes(XmlFileType.INSTANCE) // ignore usages in XML - they don't affect us
|
||||
searchReferences(classToSearch, scope) { reference ->
|
||||
val element = reference.element
|
||||
val wasProcessed = when (element.language) {
|
||||
KotlinLanguage.INSTANCE -> processClassUsageInKotlin(element)
|
||||
JavaLanguage.INSTANCE -> processClassUsageInJava(element)
|
||||
else -> {
|
||||
when (element.language.displayName) {
|
||||
"Groovy" -> {
|
||||
processClassUsageInLanguageWithPsiClass(element)
|
||||
true
|
||||
}
|
||||
"Scala" -> false
|
||||
"Clojure" -> false
|
||||
else -> {
|
||||
// If there's no PsiClass - consider processed
|
||||
element.getParentOfType<PsiClass>(true) == null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (wasProcessed) return@searchReferences true
|
||||
|
||||
if (mode != Mode.ALWAYS_SMART) {
|
||||
downShiftToPlainSearch(reference)
|
||||
return@searchReferences false
|
||||
}
|
||||
|
||||
error(getFallbackDiagnosticsMessage(reference))
|
||||
}
|
||||
|
||||
// we must use plain search inside our class (and inheritors) because implicit 'this' can happen anywhere
|
||||
(classToSearch as? KtLightClass)?.kotlinOrigin?.let { usePlainSearch(it) }
|
||||
}
|
||||
}
|
||||
addTask(ProcessClassUsagesTask(classToSearch))
|
||||
}
|
||||
|
||||
private fun getFallbackDiagnosticsMessage(reference: PsiReference): String {
|
||||
val element = reference.element
|
||||
val document = PsiDocumentManager.getInstance(project).getDocument(element.containingFile)
|
||||
val lineAndCol = PsiDiagnosticUtils.offsetToLineAndColumn(document, element.startOffset)
|
||||
return "Unsupported reference: '${element.text}' in ${element.containingFile
|
||||
.name} line ${lineAndCol.line} column ${lineAndCol.column}"
|
||||
}
|
||||
|
||||
private enum class ReferenceProcessor(val handler: (ExpressionsOfTypeProcessor, PsiReference) -> Boolean) {
|
||||
CallableOfOurType(ExpressionsOfTypeProcessor::processReferenceToCallableOfOurType),
|
||||
|
||||
ProcessLambdasInCalls({ processor, reference ->
|
||||
(reference.element as? KtReferenceExpression)?.let { processor.processLambdasForCallableReference(it) }
|
||||
true
|
||||
})
|
||||
}
|
||||
|
||||
private class StaticMemberRequestResultProcessor(val psiMember: PsiMember, classes: List<PsiClass>) :
|
||||
RequestResultProcessor(psiMember) {
|
||||
val possibleClassesNames: Set<String> = runReadAction { classes.map { it.qualifiedName }.filterNotNullTo(HashSet()) }
|
||||
|
||||
override fun processTextOccurrence(element: PsiElement, offsetInElement: Int, consumer: Processor<in PsiReference>): Boolean {
|
||||
when (element) {
|
||||
is KtQualifiedExpression -> {
|
||||
val selectorExpression = element.selectorExpression ?: return true
|
||||
val selectorReference = element.findReferenceAt(selectorExpression.startOffsetInParent)
|
||||
|
||||
val references = when (selectorReference) {
|
||||
is PsiMultiReference -> selectorReference.references.toList()
|
||||
else -> listOf(selectorReference)
|
||||
}.filterNotNull()
|
||||
|
||||
for (ref in references) {
|
||||
ProgressManager.checkCanceled()
|
||||
|
||||
if (ref.isReferenceTo(psiMember)) {
|
||||
consumer.process(ref)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is KtImportDirective -> {
|
||||
if (element.isAllUnder) {
|
||||
val fqName = element.importedFqName?.asString()
|
||||
if (fqName != null && fqName in possibleClassesNames) {
|
||||
val ref = element.importedReference
|
||||
?.getQualifiedElementSelector()
|
||||
?.references
|
||||
?.firstOrNull()
|
||||
if (ref != null) {
|
||||
consumer.process(ref)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private fun classUseScope(psiClass: PsiClass) = runReadAction {
|
||||
if (!psiClass.isValid) {
|
||||
throw ProcessCanceledException()
|
||||
}
|
||||
|
||||
val file = psiClass.containingFile
|
||||
if (file != null) file.useScope else psiClass.useScope
|
||||
}
|
||||
|
||||
private fun addStaticMemberToProcess(psiMember: PsiMember, scope: SearchScope, processor: ReferenceProcessor) {
|
||||
val declarationClass = runReadAction { psiMember.containingClass } ?: return
|
||||
val declarationName = runReadAction { psiMember.name } ?: return
|
||||
if (declarationName.isEmpty()) return
|
||||
|
||||
data class ProcessStaticCallableUsagesTask(
|
||||
val member: PsiMember,
|
||||
val memberScope: SearchScope,
|
||||
val taskProcessor: ReferenceProcessor
|
||||
) : Task {
|
||||
override fun perform() {
|
||||
// This class will look through the whole hierarchy anyway, so shouldn't be a big overhead here
|
||||
val inheritanceClasses = ClassInheritorsSearch.search(
|
||||
declarationClass,
|
||||
classUseScope(declarationClass),
|
||||
true, true, false
|
||||
).findAll()
|
||||
|
||||
val classes = (inheritanceClasses + declarationClass).filter {
|
||||
it !is KtLightClass
|
||||
}
|
||||
|
||||
val searchRequestCollector = SearchRequestCollector(SearchSession())
|
||||
val resultProcessor = StaticMemberRequestResultProcessor(member, classes)
|
||||
|
||||
val memberName = runReadAction { member.name }
|
||||
for (klass in classes) {
|
||||
val request = klass.name + "." + declarationName
|
||||
|
||||
testLog { "Searched references to static $memberName in non-Java files by request $request" }
|
||||
searchRequestCollector.searchWord(
|
||||
request,
|
||||
classUseScope(klass).intersectWith(memberScope), UsageSearchContext.IN_CODE, true, member, resultProcessor
|
||||
)
|
||||
|
||||
val qualifiedName = runReadAction { klass.qualifiedName }
|
||||
if (qualifiedName != null) {
|
||||
val importAllUnderRequest = "$qualifiedName.*"
|
||||
|
||||
testLog { "Searched references to static $memberName in non-Java files by request $importAllUnderRequest" }
|
||||
searchRequestCollector.searchWord(
|
||||
importAllUnderRequest,
|
||||
classUseScope(klass).intersectWith(memberScope), UsageSearchContext.IN_CODE, true, member, resultProcessor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
PsiSearchHelper.getInstance(project).processRequests(searchRequestCollector) { reference ->
|
||||
if (reference.element.parents.any { it is KtImportDirective }) {
|
||||
// Found declaration in import - process all file with an ordinal reference search
|
||||
val containingFile = reference.element.containingFile
|
||||
addCallableDeclarationToProcess(member, LocalSearchScope(containingFile), taskProcessor)
|
||||
|
||||
true
|
||||
} else {
|
||||
val processed = taskProcessor.handler(this@ExpressionsOfTypeProcessor, reference)
|
||||
if (!processed) { // we don't know how to handle this reference and down-shift to plain search
|
||||
downShiftToPlainSearch(reference)
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addTask(ProcessStaticCallableUsagesTask(psiMember, scope, processor))
|
||||
return
|
||||
}
|
||||
|
||||
private fun addCallableDeclarationToProcess(declaration: PsiElement, scope: SearchScope, processor: ReferenceProcessor) {
|
||||
if (scope !is LocalSearchScope && declaration is PsiMember &&
|
||||
(declaration.modifierList?.hasModifierProperty(PsiModifier.STATIC) == true)
|
||||
) {
|
||||
addStaticMemberToProcess(declaration, scope, processor)
|
||||
return
|
||||
}
|
||||
|
||||
@Suppress("NAME_SHADOWING")
|
||||
data class ProcessCallableUsagesTask(
|
||||
val declaration: PsiElement,
|
||||
val processor: ReferenceProcessor,
|
||||
val scope: SearchScope
|
||||
) : Task {
|
||||
override fun perform() {
|
||||
if (scope is LocalSearchScope) {
|
||||
testLog { "Searched imported static member $declaration in ${scope.scope.toList()}" }
|
||||
} else {
|
||||
testLog { "Searched references to ${logPresentation(declaration)} in non-Java files" }
|
||||
}
|
||||
|
||||
val searchParameters = KotlinReferencesSearchParameters(
|
||||
declaration, scope, kotlinOptions = KotlinReferencesSearchOptions(searchNamedArguments = false)
|
||||
)
|
||||
searchReferences(searchParameters) { reference ->
|
||||
val processed = processor.handler(this@ExpressionsOfTypeProcessor, reference)
|
||||
if (!processed) { // we don't know how to handle this reference and down-shift to plain search
|
||||
downShiftToPlainSearch(reference)
|
||||
}
|
||||
processed
|
||||
}
|
||||
}
|
||||
}
|
||||
addTask(ProcessCallableUsagesTask(declaration, processor, scope))
|
||||
}
|
||||
|
||||
private fun addPsiMemberTask(member: PsiMember) {
|
||||
if (!member.isPrivate() && !member.isLocal()) {
|
||||
addCallableDeclarationOfOurType(member)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addCallableDeclarationOfOurType(declaration: PsiElement) {
|
||||
addCallableDeclarationToProcess(declaration, searchScope.restrictToKotlinSources(), ReferenceProcessor.CallableOfOurType)
|
||||
}
|
||||
|
||||
/**
|
||||
* Process references to declaration which has parameter of functional type with our class used inside
|
||||
*/
|
||||
private fun addCallableDeclarationToProcessLambdasInCalls(declaration: PsiElement) {
|
||||
// we don't need to search usages of declarations in Java because Java doesn't have implicitly typed declarations so such usages cannot affect Kotlin code
|
||||
val scope = GlobalSearchScope.projectScope(project).excludeFileTypes(JavaFileType.INSTANCE, XmlFileType.INSTANCE)
|
||||
addCallableDeclarationToProcess(declaration, scope, ReferenceProcessor.ProcessLambdasInCalls)
|
||||
}
|
||||
|
||||
/**
|
||||
* Process reference to declaration whose type is our class (or our class used anywhere inside that type)
|
||||
*/
|
||||
private fun processReferenceToCallableOfOurType(reference: PsiReference) = when (reference.element.language) {
|
||||
KotlinLanguage.INSTANCE -> {
|
||||
if (reference is KtDestructuringDeclarationReference) {
|
||||
// declaration usage in form of destructuring declaration entry
|
||||
addCallableDeclarationOfOurType(reference.element)
|
||||
} else {
|
||||
(reference.element as? KtReferenceExpression)?.let { processSuspiciousExpression(it) }
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
else -> false // reference in unknown language - we don't know how to handle it
|
||||
}
|
||||
|
||||
private fun addSamInterfaceToProcess(psiClass: PsiClass) {
|
||||
if (!checkPsiClass(psiClass)) {
|
||||
return
|
||||
}
|
||||
|
||||
data class ProcessSamInterfaceTask(val psiClass: PsiClass) : Task {
|
||||
override fun perform() {
|
||||
val scope = GlobalSearchScope.projectScope(project).excludeFileTypes(KotlinFileType.INSTANCE, XmlFileType.INSTANCE)
|
||||
testLog { "Searched references to ${logPresentation(psiClass)} in non-Kotlin files" }
|
||||
searchReferences(psiClass, scope) { reference ->
|
||||
// reference in some JVM language can be method parameter (but we don't know)
|
||||
if (reference.element.language != JavaLanguage.INSTANCE) {
|
||||
downShiftToPlainSearch(reference)
|
||||
return@searchReferences false
|
||||
}
|
||||
|
||||
// check if the reference is method parameter type
|
||||
val parameter = ((reference as? PsiJavaCodeReferenceElement)?.parent as? PsiTypeElement)?.parent as? PsiParameter
|
||||
val method = parameter?.declarationScope as? PsiMethod
|
||||
if (method != null) {
|
||||
addCallableDeclarationToProcessLambdasInCalls(method)
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
addTask(ProcessSamInterfaceTask(psiClass))
|
||||
}
|
||||
|
||||
private fun processClassUsageInKotlin(element: PsiElement): Boolean {
|
||||
//TODO: type aliases
|
||||
|
||||
when (element) {
|
||||
is KtReferenceExpression -> {
|
||||
when (val parent = element.parent) {
|
||||
is KtUserType -> { // usage in type
|
||||
return processClassUsageInUserType(parent)
|
||||
}
|
||||
|
||||
is KtCallExpression -> {
|
||||
if (element == parent.calleeExpression) { // constructor invocation
|
||||
processSuspiciousExpression(parent)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
is KtContainerNode -> {
|
||||
if (parent.node.elementType == KtNodeTypes.LABEL_QUALIFIER) {
|
||||
return true // this@ClassName - it will be handled anyway because members and extensions are processed with plain search
|
||||
}
|
||||
}
|
||||
|
||||
is KtQualifiedExpression -> {
|
||||
// <class name>.memberName or some.<class name>.memberName
|
||||
if (element == parent.receiverExpression || parent.parent is KtQualifiedExpression) {
|
||||
return true // companion object member or static member access - ignore it
|
||||
}
|
||||
}
|
||||
|
||||
is KtCallableReferenceExpression -> {
|
||||
when (element) {
|
||||
parent.receiverExpression -> { // usage in receiver of callable reference (before "::") - ignore it
|
||||
return true
|
||||
}
|
||||
|
||||
parent.callableReference -> { // usage after "::" in callable reference - should be reference to constructor of our class
|
||||
processSuspiciousExpression(element)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is KtClassLiteralExpression -> {
|
||||
if (element == parent.receiverExpression) { // <class name>::class
|
||||
processSuspiciousExpression(element)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (element.getStrictParentOfType<KtImportDirective>() != null) return true // ignore usage in import
|
||||
|
||||
val bindingContext = element.analyze(BodyResolveMode.PARTIAL)
|
||||
val hasType = bindingContext.getType(element) != null
|
||||
if (hasType) { // access to object or companion object
|
||||
processSuspiciousExpression(element)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
is KDocName -> return true // ignore usage in doc-comment
|
||||
}
|
||||
|
||||
return false // unsupported type of reference
|
||||
}
|
||||
|
||||
private fun processClassUsageInUserType(userType: KtUserType): Boolean {
|
||||
val typeRef = userType.parents.lastOrNull { it is KtTypeReference }
|
||||
when (val typeRefParent = typeRef?.parent) {
|
||||
is KtCallableDeclaration -> {
|
||||
when (typeRef) {
|
||||
typeRefParent.typeReference -> { // usage in type of callable declaration
|
||||
addCallableDeclarationOfOurType(typeRefParent)
|
||||
|
||||
if (typeRefParent is KtParameter) { //TODO: what if functional type is declared with "FunctionN<...>"?
|
||||
val usedInsideFunctionalType = userType.parents.takeWhile { it != typeRef }.any { it is KtFunctionType }
|
||||
if (usedInsideFunctionalType) {
|
||||
val function = (typeRefParent.parent as? KtParameterList)?.parent as? KtFunction
|
||||
if (function != null) {
|
||||
addCallableDeclarationOfOurType(function)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
typeRefParent.receiverTypeReference -> { // usage in receiver type of callable declaration
|
||||
// we must use plain search inside extensions because implicit 'this' can happen anywhere
|
||||
usePlainSearch(typeRefParent)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is KtTypeProjection -> { // usage in type arguments of a call
|
||||
val callExpression = (typeRefParent.parent as? KtTypeArgumentList)?.parent as? KtCallExpression
|
||||
if (callExpression != null) {
|
||||
processSuspiciousExpression(callExpression)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
is KtConstructorCalleeExpression -> { // super-class name in the list of bases
|
||||
val parent = typeRefParent.parent
|
||||
if (parent is KtSuperTypeCallEntry) {
|
||||
val classOrObject = (parent.parent as KtSuperTypeList).parent as KtClassOrObject
|
||||
val psiClass = classOrObject.toLightClass()
|
||||
psiClass?.let { addClassToProcess(it) }
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
is KtSuperTypeListEntry -> { // super-interface name in the list of bases
|
||||
if (typeRef == typeRefParent.typeReference) {
|
||||
val classOrObject = (typeRefParent.parent as KtSuperTypeList).parent as KtClassOrObject
|
||||
val psiClass = classOrObject.toLightClass()
|
||||
psiClass?.let { addClassToProcess(it) }
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
is KtIsExpression -> { // <expr> is <class name>
|
||||
val scopeOfPossibleSmartCast = typeRefParent.getParentOfType<KtDeclarationWithBody>(true)
|
||||
scopeOfPossibleSmartCast?.let { usePlainSearch(it) }
|
||||
return true
|
||||
}
|
||||
|
||||
is KtWhenConditionIsPattern -> { // "is <class name>" or "!is <class name>" in when
|
||||
val whenEntry = typeRefParent.parent as KtWhenEntry
|
||||
if (typeRefParent.isNegated) {
|
||||
val whenExpression = whenEntry.parent as KtWhenExpression
|
||||
val entriesAfter = whenExpression.entries.dropWhile { it != whenEntry }.drop(1)
|
||||
entriesAfter.forEach { usePlainSearch(it) }
|
||||
} else {
|
||||
usePlainSearch(whenEntry)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
is KtBinaryExpressionWithTypeRHS -> { // <expr> as <class name>
|
||||
processSuspiciousExpression(typeRefParent)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false // unsupported case
|
||||
}
|
||||
|
||||
private fun processClassUsageInJava(element: PsiElement): Boolean {
|
||||
if (element !is PsiJavaCodeReferenceElement) return true // meaningless reference from Java
|
||||
|
||||
var prev = element
|
||||
ParentsLoop@
|
||||
for (parent in element.parents) {
|
||||
when (parent) {
|
||||
is PsiCodeBlock,
|
||||
is PsiExpression ->
|
||||
break@ParentsLoop // ignore local usages
|
||||
|
||||
is PsiMethod -> {
|
||||
if (prev == parent.returnTypeElement) { // usage in return type of a method
|
||||
addPsiMemberTask(parent)
|
||||
}
|
||||
break@ParentsLoop
|
||||
}
|
||||
|
||||
is PsiField -> {
|
||||
if (prev == parent.typeElement) { // usage in type of a field
|
||||
addPsiMemberTask(parent)
|
||||
}
|
||||
break@ParentsLoop
|
||||
}
|
||||
|
||||
is PsiReferenceList -> { // usage in extends/implements list
|
||||
if (parent.role == PsiReferenceList.Role.EXTENDS_LIST || parent.role == PsiReferenceList.Role.IMPLEMENTS_LIST) {
|
||||
val psiClass = parent.parent as PsiClass
|
||||
addNonKotlinClassToProcess(psiClass)
|
||||
}
|
||||
break@ParentsLoop
|
||||
}
|
||||
|
||||
//TODO: if Java parameter has Kotlin functional type then we should process method usages
|
||||
is PsiParameter -> {
|
||||
if (prev == parent.typeElement) { // usage in parameter type - check if the method is in SAM interface
|
||||
processParameterInSamClass(parent)
|
||||
}
|
||||
break@ParentsLoop
|
||||
}
|
||||
}
|
||||
|
||||
prev = parent
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun processClassUsageInLanguageWithPsiClass(element: PsiElement) {
|
||||
fun checkReferenceInTypeElement(typeElement: PsiTypeElement?, element: PsiElement): Boolean {
|
||||
val typeTextRange = typeElement?.textRange
|
||||
return (typeTextRange != null && element.textRange in typeTextRange)
|
||||
}
|
||||
|
||||
fun processParameter(parameter: PsiParameter): Boolean {
|
||||
if (checkReferenceInTypeElement(parameter.typeElement, element)) {
|
||||
processParameterInSamClass(parameter)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
fun processMethod(method: PsiMethod): Boolean {
|
||||
if (checkReferenceInTypeElement(method.returnTypeElement, element)) {
|
||||
addPsiMemberTask(method)
|
||||
return true
|
||||
}
|
||||
|
||||
val parameters = method.parameterList.parameters
|
||||
for (parameter in parameters) {
|
||||
if (processParameter(parameter)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
fun processField(field: PsiField): Boolean {
|
||||
if (checkReferenceInTypeElement(field.typeElement, element)) {
|
||||
addPsiMemberTask(field)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
fun processClass(psiClass: PsiClass) {
|
||||
if (!checkPsiClass(psiClass)) {
|
||||
return
|
||||
}
|
||||
|
||||
val elementTextRange: TextRange? = element.textRange
|
||||
if (elementTextRange != null) {
|
||||
val superList = listOf(psiClass.extendsList, psiClass.implementsList)
|
||||
for (psiReferenceList in superList) {
|
||||
val superListRange: TextRange? = psiReferenceList?.textRange
|
||||
if (superListRange != null && elementTextRange in superListRange) {
|
||||
addNonKotlinClassToProcess(psiClass)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (psiClass.fields.any { processField(it) }) {
|
||||
return
|
||||
}
|
||||
|
||||
if (psiClass.methods.any { processMethod(it) }) {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
val psiClass = element.getParentOfType<PsiClass>(true)
|
||||
if (psiClass != null) {
|
||||
processClass(psiClass)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processParameterInSamClass(psiParameter: PsiParameter): Boolean {
|
||||
val method = psiParameter.declarationScope as? PsiMethod ?: return false
|
||||
|
||||
if (method.hasModifierProperty(PsiModifier.ABSTRACT)) {
|
||||
val psiClass = method.containingClass
|
||||
if (psiClass != null) {
|
||||
testLog { "Resolved java class to descriptor: ${psiClass.qualifiedName}" }
|
||||
|
||||
val classDescriptor = psiClass.getJavaMemberDescriptor() as? JavaClassDescriptor
|
||||
if (classDescriptor != null && getSingleAbstractMethodOrNull(classDescriptor) != null) {
|
||||
addSamInterfaceToProcess(psiClass)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Process expression which may have type of our class (or our class used anywhere inside that type)
|
||||
*/
|
||||
private fun processSuspiciousExpression(expression: KtExpression) {
|
||||
var inScope = expression in searchScope
|
||||
var affectedScope: PsiElement = expression
|
||||
ParentsLoop@
|
||||
for (element in expression.parentsWithSelf) {
|
||||
affectedScope = element
|
||||
if (element !is KtExpression) continue
|
||||
|
||||
if (searchScope is LocalSearchScope) { // optimization to not check every expression
|
||||
inScope = inScope && element in searchScope
|
||||
}
|
||||
if (inScope) {
|
||||
possibleMatchHandler(element)
|
||||
}
|
||||
|
||||
when (val parent = element.parent) {
|
||||
is KtDestructuringDeclaration -> { // "val (x, y) = <expr>"
|
||||
processSuspiciousDeclaration(parent)
|
||||
break@ParentsLoop
|
||||
}
|
||||
|
||||
is KtDeclarationWithInitializer -> { // "val x = <expr>" or "fun f() = <expr>"
|
||||
if (element == parent.initializer) {
|
||||
processSuspiciousDeclaration(parent)
|
||||
}
|
||||
break@ParentsLoop
|
||||
}
|
||||
|
||||
is KtContainerNode -> {
|
||||
if (parent.node.elementType == KtNodeTypes.LOOP_RANGE) { // "for (x in <expr>) ..."
|
||||
val forExpression = parent.parent as KtForExpression
|
||||
(forExpression.destructuringDeclaration ?: forExpression.loopParameter as KtDeclaration?)?.let {
|
||||
processSuspiciousDeclaration(it)
|
||||
}
|
||||
break@ParentsLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!element.mayTypeAffectAncestors()) break
|
||||
}
|
||||
|
||||
// use plain search in all lambdas and anonymous functions inside because they parameters or receiver can be implicitly typed with our class
|
||||
usePlainSearchInLambdas(affectedScope)
|
||||
}
|
||||
|
||||
private fun processLambdasForCallableReference(expression: KtReferenceExpression) {
|
||||
//TODO: receiver?
|
||||
usePlainSearchInLambdas(expression.parent)
|
||||
}
|
||||
|
||||
/**
|
||||
* Process declaration which may have implicit type of our class (or our class used anywhere inside that type)
|
||||
*/
|
||||
private fun processSuspiciousDeclaration(declaration: KtDeclaration) {
|
||||
if (declaration is KtDestructuringDeclaration) {
|
||||
declaration.entries.forEach { processSuspiciousDeclaration(it) }
|
||||
} else {
|
||||
if (!isImplicitlyTyped(declaration)) return
|
||||
|
||||
testLog { "Checked type of ${logPresentation(declaration)}" }
|
||||
|
||||
val descriptor = declaration.resolveToDescriptorIfAny() as? CallableDescriptor ?: return
|
||||
val type = descriptor.returnType
|
||||
if (type != null && type.containsTypeOrDerivedInside(typeToSearch)) {
|
||||
addCallableDeclarationOfOurType(declaration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun usePlainSearchInLambdas(scope: PsiElement) {
|
||||
scope.forEachDescendantOfType<KtFunction> {
|
||||
if (it.nameIdentifier == null) {
|
||||
usePlainSearch(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun usePlainSearch(scope: KtElement) {
|
||||
runReadAction {
|
||||
if (!scope.isValid) return@runReadAction
|
||||
|
||||
val file = scope.containingKtFile
|
||||
val restricted = LocalSearchScope(scope).intersectWith(searchScope)
|
||||
if (restricted is LocalSearchScope) {
|
||||
ScopeLoop@
|
||||
for (element in restricted.scope) {
|
||||
val prevElements = scopesToUsePlainSearch.getOrPut(file) { ArrayList() }
|
||||
for ((index, prevElement) in prevElements.withIndex()) {
|
||||
if (!prevElement.isValid) continue@ScopeLoop
|
||||
if (prevElement.isAncestor(element, strict = false)) continue@ScopeLoop
|
||||
if (element.isAncestor(prevElement)) {
|
||||
prevElements[index] = element
|
||||
continue@ScopeLoop
|
||||
}
|
||||
}
|
||||
prevElements.add(element)
|
||||
}
|
||||
} else {
|
||||
assert(restricted == GlobalSearchScope.EMPTY_SCOPE)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: code is quite similar to PartialBodyResolveFilter.isValueNeeded
|
||||
private fun KtExpression.mayTypeAffectAncestors(): Boolean {
|
||||
when (val parent = this.parent) {
|
||||
is KtBlockExpression -> {
|
||||
return this == parent.statements.last() && parent.mayTypeAffectAncestors()
|
||||
}
|
||||
|
||||
is KtDeclarationWithBody -> {
|
||||
if (this == parent.bodyExpression) {
|
||||
return !parent.hasBlockBody() && !parent.hasDeclaredReturnType()
|
||||
}
|
||||
}
|
||||
|
||||
is KtContainerNode -> {
|
||||
val grandParent = parent.parent
|
||||
return when (parent.node.elementType) {
|
||||
KtNodeTypes.CONDITION, KtNodeTypes.BODY -> false
|
||||
KtNodeTypes.THEN, KtNodeTypes.ELSE -> (grandParent as KtExpression).mayTypeAffectAncestors()
|
||||
KtNodeTypes.LOOP_RANGE, KtNodeTypes.INDICES -> true
|
||||
else -> true // something else unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
return true // we don't know
|
||||
}
|
||||
|
||||
private fun KotlinType.containsTypeOrDerivedInside(type: FuzzyType): Boolean {
|
||||
return type.checkIsSuperTypeOf(this) != null || arguments.any { !it.isStarProjection && it.type.containsTypeOrDerivedInside(type) }
|
||||
}
|
||||
|
||||
private fun isImplicitlyTyped(declaration: KtDeclaration): Boolean {
|
||||
return when (declaration) {
|
||||
is KtFunction -> !declaration.hasDeclaredReturnType()
|
||||
is KtVariableDeclaration -> declaration.typeReference == null
|
||||
is KtParameter -> declaration.typeReference == null
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun searchReferences(element: PsiElement, scope: SearchScope, processor: (PsiReference) -> Boolean) {
|
||||
val parameters = ReferencesSearch.SearchParameters(element, scope, false)
|
||||
searchReferences(parameters, processor)
|
||||
}
|
||||
|
||||
private fun searchReferences(parameters: ReferencesSearch.SearchParameters, processor: (PsiReference) -> Boolean) {
|
||||
ReferencesSearch.search(parameters).forEach(Processor { ref ->
|
||||
ProgressManager.checkCanceled()
|
||||
runReadAction {
|
||||
if (ref.element.isValid) {
|
||||
processor(ref)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* 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.search.usagesSearch.operators
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.psi.search.SearchRequestCollector
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
|
||||
import org.jetbrains.kotlin.lexer.KtSingleValueToken
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
|
||||
class BinaryOperatorReferenceSearcher(
|
||||
targetFunction: PsiElement,
|
||||
private val operationTokens: List<KtSingleValueToken>,
|
||||
searchScope: SearchScope,
|
||||
consumer: Processor<in PsiReference>,
|
||||
optimizer: SearchRequestCollector,
|
||||
options: KotlinReferencesSearchOptions
|
||||
) : OperatorReferenceSearcher<KtBinaryExpression>(
|
||||
targetFunction,
|
||||
searchScope,
|
||||
consumer,
|
||||
optimizer,
|
||||
options,
|
||||
wordsToSearch = operationTokens.map { it.value }) {
|
||||
|
||||
override fun processPossibleReceiverExpression(expression: KtExpression) {
|
||||
val binaryExpression = expression.parent as? KtBinaryExpression ?: return
|
||||
if (binaryExpression.operationToken !in operationTokens) return
|
||||
if (expression != binaryExpression.left) return
|
||||
processReferenceElement(binaryExpression)
|
||||
}
|
||||
|
||||
override fun isReferenceToCheck(ref: PsiReference): Boolean {
|
||||
if (ref !is KtSimpleNameReference) return false
|
||||
val element = ref.element
|
||||
if (element.parent !is KtBinaryExpression) return false
|
||||
return element.getReferencedNameElementType() in operationTokens
|
||||
}
|
||||
|
||||
override fun extractReference(element: KtElement): PsiReference? {
|
||||
val binaryExpression = element as? KtBinaryExpression ?: return null
|
||||
if (binaryExpression.operationToken !in operationTokens) return null
|
||||
return binaryExpression.operationReference.references.firstIsInstance<KtSimpleNameReference>()
|
||||
}
|
||||
}
|
||||
-74
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* 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.search.usagesSearch.operators
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.psi.search.SearchRequestCollector
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
|
||||
class ContainsOperatorReferenceSearcher(
|
||||
targetFunction: PsiElement,
|
||||
searchScope: SearchScope,
|
||||
consumer: Processor<in PsiReference>,
|
||||
optimizer: SearchRequestCollector,
|
||||
options: KotlinReferencesSearchOptions
|
||||
) : OperatorReferenceSearcher<KtOperationReferenceExpression>(
|
||||
targetFunction,
|
||||
searchScope,
|
||||
consumer,
|
||||
optimizer,
|
||||
options,
|
||||
wordsToSearch = listOf("in")
|
||||
) {
|
||||
private companion object {
|
||||
private val OPERATION_TOKENS = setOf(KtTokens.IN_KEYWORD, KtTokens.NOT_IN)
|
||||
}
|
||||
|
||||
override fun processPossibleReceiverExpression(expression: KtExpression) {
|
||||
val parent = expression.parent
|
||||
when (parent) {
|
||||
is KtBinaryExpression -> {
|
||||
if (parent.operationToken in OPERATION_TOKENS && expression == parent.right) {
|
||||
processReferenceElement(parent.operationReference)
|
||||
}
|
||||
}
|
||||
|
||||
is KtWhenConditionInRange -> {
|
||||
processReferenceElement(parent.operationReference)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun isReferenceToCheck(ref: PsiReference): Boolean {
|
||||
if (ref !is KtSimpleNameReference) return false
|
||||
val element = ref.element as? KtOperationReferenceExpression ?: return false
|
||||
return element.getReferencedNameElementType() in OPERATION_TOKENS
|
||||
}
|
||||
|
||||
override fun extractReference(element: KtElement): PsiReference? {
|
||||
val referenceExpression = element as? KtOperationReferenceExpression ?: return null
|
||||
if (referenceExpression.getReferencedNameElementType() !in OPERATION_TOKENS) return null
|
||||
return referenceExpression.references.firstIsInstance<KtSimpleNameReference>()
|
||||
}
|
||||
}
|
||||
-85
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* 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.search.usagesSearch.operators
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.psi.search.SearchRequestCollector
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.dataClassComponentFunction
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
|
||||
class DestructuringDeclarationReferenceSearcher(
|
||||
targetDeclaration: PsiElement,
|
||||
private val componentIndex: Int,
|
||||
searchScope: SearchScope,
|
||||
consumer: Processor<in PsiReference>,
|
||||
optimizer: SearchRequestCollector,
|
||||
options: KotlinReferencesSearchOptions
|
||||
) : OperatorReferenceSearcher<KtDestructuringDeclaration>(
|
||||
targetDeclaration,
|
||||
searchScope,
|
||||
consumer,
|
||||
optimizer,
|
||||
options,
|
||||
wordsToSearch = listOf("(")
|
||||
) {
|
||||
|
||||
override fun resolveTargetToDescriptor(): FunctionDescriptor? {
|
||||
return if (targetDeclaration is KtParameter) {
|
||||
targetDeclaration.dataClassComponentFunction()
|
||||
} else {
|
||||
super.resolveTargetToDescriptor()
|
||||
}
|
||||
}
|
||||
|
||||
override fun extractReference(element: KtElement): PsiReference? {
|
||||
val destructuringDeclaration = element as? KtDestructuringDeclaration ?: return null
|
||||
val entries = destructuringDeclaration.entries
|
||||
if (entries.size < componentIndex) return null
|
||||
return entries[componentIndex - 1].references.firstIsInstance<KtDestructuringDeclarationReference>()
|
||||
}
|
||||
|
||||
override fun isReferenceToCheck(ref: PsiReference) = ref is KtDestructuringDeclarationReference
|
||||
|
||||
override fun processPossibleReceiverExpression(expression: KtExpression) {
|
||||
val parent = expression.parent
|
||||
val destructuringDeclaration = when (parent) {
|
||||
is KtDestructuringDeclaration -> parent
|
||||
|
||||
is KtContainerNode -> {
|
||||
if (parent.node.elementType == KtNodeTypes.LOOP_RANGE) {
|
||||
(parent.parent as KtForExpression).destructuringDeclaration
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (destructuringDeclaration != null) {
|
||||
processReferenceElement(destructuringDeclaration)
|
||||
}
|
||||
}
|
||||
}
|
||||
-68
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* 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.search.usagesSearch.operators
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.psi.search.SearchRequestCollector
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.kotlin.idea.references.KtArrayAccessReference
|
||||
import org.jetbrains.kotlin.idea.references.readWriteAccess
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
|
||||
import org.jetbrains.kotlin.psi.KtArrayAccessExpression
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
|
||||
class IndexingOperatorReferenceSearcher(
|
||||
targetFunction: PsiElement,
|
||||
searchScope: SearchScope,
|
||||
consumer: Processor<in PsiReference>,
|
||||
optimizer: SearchRequestCollector,
|
||||
options: KotlinReferencesSearchOptions,
|
||||
private val isSet: Boolean
|
||||
) : OperatorReferenceSearcher<KtArrayAccessExpression>(
|
||||
targetFunction,
|
||||
searchScope,
|
||||
consumer,
|
||||
optimizer,
|
||||
options,
|
||||
wordsToSearch = listOf("[")
|
||||
) {
|
||||
|
||||
override fun processPossibleReceiverExpression(expression: KtExpression) {
|
||||
val accessExpression = expression.parent as? KtArrayAccessExpression ?: return
|
||||
if (expression != accessExpression.arrayExpression) return
|
||||
if (!checkAccessExpression(accessExpression)) return
|
||||
processReferenceElement(accessExpression)
|
||||
}
|
||||
|
||||
override fun isReferenceToCheck(ref: PsiReference) =
|
||||
ref is KtArrayAccessReference && checkAccessExpression(ref.element)
|
||||
|
||||
override fun extractReference(element: KtElement): PsiReference? {
|
||||
val accessExpression = element as? KtArrayAccessExpression ?: return null
|
||||
if (!checkAccessExpression(accessExpression)) return null
|
||||
return accessExpression.references.firstIsInstance<KtArrayAccessReference>()
|
||||
}
|
||||
|
||||
private fun checkAccessExpression(accessExpression: KtArrayAccessExpression): Boolean {
|
||||
val readWriteAccess = accessExpression.readWriteAccess(useResolveForReadWrite = false)
|
||||
return if (isSet) readWriteAccess.isWrite else readWriteAccess.isRead
|
||||
}
|
||||
}
|
||||
-92
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* 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.search.usagesSearch.operators
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.psi.search.SearchRequestCollector
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.kotlin.idea.references.KtInvokeFunctionReference
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
|
||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isExtensionDeclaration
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
import org.jetbrains.uast.UMethod
|
||||
import org.jetbrains.uast.UastContext
|
||||
import org.jetbrains.uast.convertOpt
|
||||
|
||||
class InvokeOperatorReferenceSearcher(
|
||||
targetFunction: PsiElement,
|
||||
searchScope: SearchScope,
|
||||
consumer: Processor<in PsiReference>,
|
||||
optimizer: SearchRequestCollector,
|
||||
options: KotlinReferencesSearchOptions
|
||||
) : OperatorReferenceSearcher<KtCallExpression>(targetFunction, searchScope, consumer, optimizer, options, wordsToSearch = emptyList()) {
|
||||
private val callArgumentsSize: Int?
|
||||
|
||||
init {
|
||||
val uastContext = ServiceManager.getService<UastContext>(targetFunction.project, UastContext::class.java)
|
||||
callArgumentsSize = when {
|
||||
uastContext != null -> {
|
||||
val uMethod = uastContext.convertOpt<UMethod>(targetDeclaration, null)
|
||||
val uastParameters = uMethod?.uastParameters
|
||||
|
||||
if (uastParameters != null) {
|
||||
val isStableNumberOfArguments = uastParameters.none { uParameter ->
|
||||
@Suppress("UElementAsPsi")
|
||||
uParameter.uastInitializer != null || uParameter.isVarArgs
|
||||
}
|
||||
|
||||
if (isStableNumberOfArguments) {
|
||||
val numberOfArguments = uastParameters.size
|
||||
when {
|
||||
targetFunction.isExtensionDeclaration() -> numberOfArguments - 1
|
||||
else -> numberOfArguments
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
override fun processPossibleReceiverExpression(expression: KtExpression) {
|
||||
val callExpression = expression.parent as? KtCallExpression ?: return
|
||||
processReferenceElement(callExpression)
|
||||
}
|
||||
|
||||
override fun isReferenceToCheck(ref: PsiReference) = ref is KtInvokeFunctionReference
|
||||
|
||||
override fun extractReference(element: KtElement): PsiReference? {
|
||||
val callExpression = element as? KtCallExpression ?: return null
|
||||
|
||||
if (callArgumentsSize != null && callArgumentsSize != callExpression.valueArguments.size) {
|
||||
return null
|
||||
}
|
||||
|
||||
return callExpression.references.firstIsInstance<KtInvokeFunctionReference>()
|
||||
}
|
||||
}
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* 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.search.usagesSearch.operators
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.psi.search.SearchRequestCollector
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.idea.references.KtForLoopInReference
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
|
||||
class IteratorOperatorReferenceSearcher(
|
||||
targetFunction: PsiElement,
|
||||
searchScope: SearchScope,
|
||||
consumer: Processor<in PsiReference>,
|
||||
optimizer: SearchRequestCollector,
|
||||
options: KotlinReferencesSearchOptions
|
||||
) : OperatorReferenceSearcher<KtForExpression>(targetFunction, searchScope, consumer, optimizer, options, wordsToSearch = listOf("in")) {
|
||||
|
||||
override fun processPossibleReceiverExpression(expression: KtExpression) {
|
||||
val parent = expression.parent
|
||||
if (parent.node.elementType == KtNodeTypes.LOOP_RANGE) {
|
||||
processReferenceElement(parent.parent as KtForExpression)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isReferenceToCheck(ref: PsiReference): Boolean {
|
||||
return ref is KtForLoopInReference
|
||||
}
|
||||
|
||||
override fun extractReference(element: KtElement): PsiReference? {
|
||||
return (element as? KtForExpression)?.references?.firstIsInstance<KtForLoopInReference>()
|
||||
}
|
||||
}
|
||||
-374
@@ -1,374 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.search.usagesSearch.operators
|
||||
|
||||
import com.intellij.lang.java.JavaLanguage
|
||||
import com.intellij.openapi.progress.ProgressIndicatorProvider
|
||||
import com.intellij.openapi.progress.util.ProgressWrapper
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.search.*
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
|
||||
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
||||
import org.jetbrains.kotlin.asJava.toLightClass
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.KotlinIdeaAnalysisBundle
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.util.getJavaOrKotlinMemberDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.util.hasJavaResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinRequestResultProcessor
|
||||
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.ExpressionsOfTypeProcessor
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.ExpressionsOfTypeProcessor.Companion.logPresentation
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.ExpressionsOfTypeProcessor.Companion.testLog
|
||||
import org.jetbrains.kotlin.idea.util.FuzzyType
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.idea.util.fuzzyExtensionReceiverType
|
||||
import org.jetbrains.kotlin.idea.util.toFuzzyType
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
|
||||
import org.jetbrains.kotlin.resolve.DataClassDescriptorResolver
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.util.isValidOperator
|
||||
import java.util.*
|
||||
|
||||
abstract class OperatorReferenceSearcher<TReferenceElement : KtElement>(
|
||||
protected val targetDeclaration: PsiElement,
|
||||
private val searchScope: SearchScope,
|
||||
private val consumer: Processor<in PsiReference>,
|
||||
private val optimizer: SearchRequestCollector,
|
||||
private val options: KotlinReferencesSearchOptions,
|
||||
private val wordsToSearch: List<String>
|
||||
) {
|
||||
private val project = targetDeclaration.project
|
||||
|
||||
/**
|
||||
* Invoked for all expressions that may have type matching receiver type of our operator
|
||||
*/
|
||||
protected abstract fun processPossibleReceiverExpression(expression: KtExpression)
|
||||
|
||||
/**
|
||||
* Extract reference that may resolve to our operator (no actual resolve to be performed)
|
||||
*/
|
||||
protected abstract fun extractReference(element: KtElement): PsiReference?
|
||||
|
||||
/**
|
||||
* Check if reference may potentially resolve to our operator (no actual resolve to be performed)
|
||||
*/
|
||||
protected abstract fun isReferenceToCheck(ref: PsiReference): Boolean
|
||||
|
||||
protected fun processReferenceElement(element: TReferenceElement): Boolean {
|
||||
val reference = extractReference(element) ?: return true
|
||||
testLog { "Resolved ${logPresentation(element)}" }
|
||||
return if (reference.isReferenceTo(targetDeclaration)) {
|
||||
consumer.process(reference)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun create(
|
||||
declaration: PsiElement,
|
||||
searchScope: SearchScope,
|
||||
consumer: Processor<in PsiReference>,
|
||||
optimizer: SearchRequestCollector,
|
||||
options: KotlinReferencesSearchOptions
|
||||
): OperatorReferenceSearcher<*>? {
|
||||
return runReadAction {
|
||||
if (declaration.isValid)
|
||||
createInReadAction(declaration, searchScope, consumer, optimizer, options)
|
||||
else
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun createInReadAction(
|
||||
declaration: PsiElement,
|
||||
searchScope: SearchScope,
|
||||
consumer: Processor<in PsiReference>,
|
||||
optimizer: SearchRequestCollector,
|
||||
options: KotlinReferencesSearchOptions
|
||||
): OperatorReferenceSearcher<*>? {
|
||||
val functionName = when (declaration) {
|
||||
is KtNamedFunction -> declaration.name
|
||||
is PsiMethod -> declaration.name
|
||||
else -> null
|
||||
} ?: return null
|
||||
|
||||
if (!Name.isValidIdentifier(functionName)) return null
|
||||
val name = Name.identifier(functionName)
|
||||
|
||||
val declarationToUse = if (declaration is KtLightMethod) {
|
||||
declaration.kotlinOrigin ?: return null
|
||||
} else {
|
||||
declaration
|
||||
}
|
||||
|
||||
return createInReadAction(declarationToUse, name, consumer, optimizer, options, searchScope)
|
||||
}
|
||||
|
||||
private fun createInReadAction(
|
||||
declaration: PsiElement,
|
||||
name: Name,
|
||||
consumer: Processor<in PsiReference>,
|
||||
optimizer: SearchRequestCollector,
|
||||
options: KotlinReferencesSearchOptions,
|
||||
searchScope: SearchScope
|
||||
): OperatorReferenceSearcher<*>? {
|
||||
if (DataClassDescriptorResolver.isComponentLike(name)) {
|
||||
if (!options.searchForComponentConventions) return null
|
||||
val componentIndex = DataClassDescriptorResolver.getComponentIndex(name.asString())
|
||||
return DestructuringDeclarationReferenceSearcher(declaration, componentIndex, searchScope, consumer, optimizer, options)
|
||||
}
|
||||
|
||||
if (!options.searchForOperatorConventions) return null
|
||||
|
||||
val binaryOp = OperatorConventions.BINARY_OPERATION_NAMES.inverse()[name]
|
||||
val assignmentOp = OperatorConventions.ASSIGNMENT_OPERATIONS.inverse()[name]
|
||||
val unaryOp = OperatorConventions.UNARY_OPERATION_NAMES.inverse()[name]
|
||||
|
||||
when {
|
||||
binaryOp != null -> {
|
||||
val counterpartAssignmentOp = OperatorConventions.ASSIGNMENT_OPERATION_COUNTERPARTS.inverse()[binaryOp]
|
||||
val operationTokens = listOfNotNull(binaryOp, counterpartAssignmentOp)
|
||||
return BinaryOperatorReferenceSearcher(declaration, operationTokens, searchScope, consumer, optimizer, options)
|
||||
}
|
||||
|
||||
assignmentOp != null ->
|
||||
return BinaryOperatorReferenceSearcher(declaration, listOf(assignmentOp), searchScope, consumer, optimizer, options)
|
||||
|
||||
unaryOp != null ->
|
||||
return UnaryOperatorReferenceSearcher(declaration, unaryOp, searchScope, consumer, optimizer, options)
|
||||
|
||||
name == OperatorNameConventions.INVOKE ->
|
||||
return InvokeOperatorReferenceSearcher(declaration, searchScope, consumer, optimizer, options)
|
||||
|
||||
name == OperatorNameConventions.GET ->
|
||||
return IndexingOperatorReferenceSearcher(declaration, searchScope, consumer, optimizer, options, isSet = false)
|
||||
|
||||
name == OperatorNameConventions.SET ->
|
||||
return IndexingOperatorReferenceSearcher(declaration, searchScope, consumer, optimizer, options, isSet = true)
|
||||
|
||||
name == OperatorNameConventions.CONTAINS ->
|
||||
return ContainsOperatorReferenceSearcher(declaration, searchScope, consumer, optimizer, options)
|
||||
|
||||
name == OperatorNameConventions.EQUALS ->
|
||||
return BinaryOperatorReferenceSearcher(
|
||||
declaration,
|
||||
listOf(KtTokens.EQEQ, KtTokens.EXCLEQ),
|
||||
searchScope,
|
||||
consumer,
|
||||
optimizer,
|
||||
options
|
||||
)
|
||||
|
||||
name == OperatorNameConventions.COMPARE_TO ->
|
||||
return BinaryOperatorReferenceSearcher(
|
||||
declaration,
|
||||
listOf(KtTokens.LT, KtTokens.GT, KtTokens.LTEQ, KtTokens.GTEQ),
|
||||
searchScope,
|
||||
consumer,
|
||||
optimizer,
|
||||
options
|
||||
)
|
||||
|
||||
name == OperatorNameConventions.ITERATOR ->
|
||||
return IteratorOperatorReferenceSearcher(declaration, searchScope, consumer, optimizer, options)
|
||||
|
||||
name == OperatorNameConventions.GET_VALUE || name == OperatorNameConventions.SET_VALUE || name == OperatorNameConventions.PROVIDE_DELEGATE ->
|
||||
return PropertyDelegationOperatorReferenceSearcher(declaration, searchScope, consumer, optimizer, options)
|
||||
|
||||
else ->
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private object SearchesInProgress : ThreadLocal<HashSet<PsiElement>>() {
|
||||
override fun initialValue() = HashSet<PsiElement>()
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun resolveTargetToDescriptor(): FunctionDescriptor? {
|
||||
return when {
|
||||
targetDeclaration is KtDeclaration -> targetDeclaration.resolveToDescriptorIfAny(BodyResolveMode.FULL)
|
||||
targetDeclaration is PsiMember && targetDeclaration.hasJavaResolutionFacade() ->
|
||||
targetDeclaration.getJavaOrKotlinMemberDescriptor()
|
||||
else -> null
|
||||
} as? FunctionDescriptor
|
||||
}
|
||||
|
||||
fun run() {
|
||||
val receiverType = runReadAction { extractReceiverType() } ?: return
|
||||
val psiClass = runReadAction { receiverType.toPsiClass() }
|
||||
|
||||
val inProgress = SearchesInProgress.get()
|
||||
if (psiClass != null) {
|
||||
if (!inProgress.add(psiClass)) {
|
||||
testLog {
|
||||
"ExpressionOfTypeProcessor is already started for ${runReadAction { psiClass.qualifiedName }}. Exit for operator ${logPresentation(
|
||||
targetDeclaration
|
||||
)}."
|
||||
}
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if (!inProgress.add(targetDeclaration)) {
|
||||
testLog { "ExpressionOfTypeProcessor is already started for operator ${logPresentation(targetDeclaration)}. Exit." }
|
||||
return //TODO: it's not quite correct
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
ExpressionsOfTypeProcessor(
|
||||
receiverType,
|
||||
psiClass,
|
||||
searchScope,
|
||||
project,
|
||||
possibleMatchHandler = { expression -> processPossibleReceiverExpression(expression) },
|
||||
possibleMatchesInScopeHandler = { searchScope -> doPlainSearch(searchScope) }
|
||||
).run()
|
||||
} finally {
|
||||
inProgress.remove(psiClass ?: targetDeclaration)
|
||||
}
|
||||
}
|
||||
|
||||
private fun FuzzyType.toPsiClass(): PsiClass? {
|
||||
val classDescriptor = type.constructor.declarationDescriptor ?: return null
|
||||
val classDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, classDescriptor)
|
||||
return when (classDeclaration) {
|
||||
is PsiClass -> classDeclaration
|
||||
is KtClassOrObject -> classDeclaration.toLightClass()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractReceiverType(): FuzzyType? {
|
||||
val descriptor = resolveTargetToDescriptor()?.takeIf { it.isValidOperator() } ?: return null
|
||||
|
||||
return if (descriptor.isExtension) {
|
||||
descriptor.fuzzyExtensionReceiverType()!!
|
||||
} else {
|
||||
val classDescriptor = descriptor.containingDeclaration as? ClassDescriptor ?: return null
|
||||
classDescriptor.defaultType.toFuzzyType(classDescriptor.typeConstructor.parameters)
|
||||
}
|
||||
}
|
||||
|
||||
private fun doPlainSearch(scope: SearchScope) {
|
||||
testLog { "Used plain search of ${logPresentation(targetDeclaration)} in ${scope.logPresentation()}" }
|
||||
|
||||
if (scope is LocalSearchScope) {
|
||||
for (element in scope.scope) {
|
||||
if (element is KtElement) {
|
||||
runReadAction {
|
||||
if (element.isValid) {
|
||||
val refs = ArrayList<PsiReference>()
|
||||
val elements = element.collectDescendantsOfType<KtElement> {
|
||||
val ref = extractReference(it) ?: return@collectDescendantsOfType false
|
||||
refs.add(ref)
|
||||
true
|
||||
}
|
||||
|
||||
// resolve all references at once
|
||||
(element.containingFile as KtFile).getResolutionFacade().analyze(elements, BodyResolveMode.PARTIAL)
|
||||
|
||||
refs
|
||||
.filter { it.isReferenceTo(targetDeclaration) }
|
||||
.forEach { consumer.process(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
scope as GlobalSearchScope
|
||||
if (wordsToSearch.isNotEmpty()) {
|
||||
val unwrappedElement = targetDeclaration.namedUnwrappedElement ?: return
|
||||
val resultProcessor = KotlinRequestResultProcessor(
|
||||
unwrappedElement,
|
||||
filter = { ref -> isReferenceToCheck(ref) },
|
||||
options = options
|
||||
)
|
||||
wordsToSearch.forEach {
|
||||
optimizer.searchWord(
|
||||
it,
|
||||
scope.restrictToKotlinSources(),
|
||||
UsageSearchContext.IN_CODE,
|
||||
true,
|
||||
unwrappedElement,
|
||||
resultProcessor
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val psiManager = PsiManager.getInstance(project)
|
||||
// we must unwrap progress indicator because ProgressWrapper does not do anything on changing text and fraction
|
||||
val progress = ProgressWrapper.unwrap(ProgressIndicatorProvider.getGlobalProgressIndicator())
|
||||
progress?.pushState()
|
||||
progress?.text = KotlinIdeaAnalysisBundle.message("searching.for.implicit.usages")
|
||||
|
||||
try {
|
||||
val files = runReadAction { FileTypeIndex.getFiles(KotlinFileType.INSTANCE, scope) }
|
||||
for ((index, file) in files.withIndex()) {
|
||||
progress?.checkCanceled()
|
||||
runReadAction {
|
||||
if (file.isValid) {
|
||||
progress?.fraction = index / files.size.toDouble()
|
||||
progress?.text2 = file.path
|
||||
val psiFile = psiManager.findFile(file) as? KtFile
|
||||
if (psiFile != null) {
|
||||
doPlainSearch(LocalSearchScope(psiFile))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
progress?.popState()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun SearchScope.logPresentation(): String {
|
||||
return when (this) {
|
||||
searchScope -> "whole search scope"
|
||||
|
||||
is LocalSearchScope -> {
|
||||
scope
|
||||
.map { element ->
|
||||
" " + runReadAction {
|
||||
when (element) {
|
||||
is KtFunctionLiteral -> element.text
|
||||
is KtWhenEntry -> {
|
||||
if (element.isElse)
|
||||
"KtWhenEntry \"else\""
|
||||
else
|
||||
"KtWhenEntry \"" + element.conditions.joinToString(", ") { it.text } + "\""
|
||||
}
|
||||
is KtNamedDeclaration -> element.node.elementType.toString() + ":" + element.name
|
||||
else -> element.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
.toList()
|
||||
.sorted()
|
||||
.joinToString("\n", "LocalSearchScope:\n")
|
||||
}
|
||||
|
||||
else -> this.displayName
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* 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.search.usagesSearch.operators
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.psi.search.SearchRequestCollector
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.kotlin.idea.references.KtPropertyDelegationMethodsReference
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtPropertyDelegate
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
|
||||
class PropertyDelegationOperatorReferenceSearcher(
|
||||
targetFunction: PsiElement,
|
||||
searchScope: SearchScope,
|
||||
consumer: Processor<in PsiReference>,
|
||||
optimizer: SearchRequestCollector,
|
||||
options: KotlinReferencesSearchOptions
|
||||
) : OperatorReferenceSearcher<KtPropertyDelegate>(targetFunction, searchScope, consumer, optimizer, options, wordsToSearch = listOf("by")) {
|
||||
|
||||
override fun processPossibleReceiverExpression(expression: KtExpression) {
|
||||
(expression.parent as? KtPropertyDelegate)?.let { processReferenceElement(it) }
|
||||
}
|
||||
|
||||
override fun isReferenceToCheck(ref: PsiReference): Boolean {
|
||||
return ref is KtPropertyDelegationMethodsReference
|
||||
}
|
||||
|
||||
override fun extractReference(element: KtElement): PsiReference? {
|
||||
return (element as? KtPropertyDelegate)?.references?.firstIsInstance<KtPropertyDelegationMethodsReference>()
|
||||
}
|
||||
}
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* 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.search.usagesSearch.operators
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.psi.search.SearchRequestCollector
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
|
||||
import org.jetbrains.kotlin.lexer.KtSingleValueToken
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtUnaryExpression
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
|
||||
class UnaryOperatorReferenceSearcher(
|
||||
targetFunction: PsiElement,
|
||||
private val operationToken: KtSingleValueToken,
|
||||
searchScope: SearchScope,
|
||||
consumer: Processor<in PsiReference>,
|
||||
optimizer: SearchRequestCollector,
|
||||
options: KotlinReferencesSearchOptions
|
||||
) : OperatorReferenceSearcher<KtUnaryExpression>(
|
||||
targetFunction,
|
||||
searchScope,
|
||||
consumer,
|
||||
optimizer,
|
||||
options,
|
||||
wordsToSearch = listOf(operationToken.value)
|
||||
) {
|
||||
|
||||
override fun processPossibleReceiverExpression(expression: KtExpression) {
|
||||
val unaryExpression = expression.parent as? KtUnaryExpression ?: return
|
||||
if (unaryExpression.operationToken != operationToken) return
|
||||
processReferenceElement(unaryExpression)
|
||||
}
|
||||
|
||||
override fun isReferenceToCheck(ref: PsiReference): Boolean {
|
||||
if (ref !is KtSimpleNameReference) return false
|
||||
val element = ref.element
|
||||
if (element.parent !is KtUnaryExpression) return false
|
||||
return element.getReferencedNameElementType() == operationToken
|
||||
}
|
||||
|
||||
override fun extractReference(element: KtElement): PsiReference? {
|
||||
val unaryExpression = element as? KtUnaryExpression ?: return null
|
||||
if (unaryExpression.operationToken != operationToken) return null
|
||||
return unaryExpression.operationReference.references.firstIsInstance<KtSimpleNameReference>()
|
||||
}
|
||||
}
|
||||
+32
-4
@@ -16,14 +16,14 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.search.usagesSearch
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiNamedElement
|
||||
import org.jetbrains.kotlin.asJava.LightClassUtil
|
||||
import org.jetbrains.kotlin.asJava.LightClassUtil.PropertyAccessorsPsiMethods
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToParameterDescriptorIfAny
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
@@ -31,6 +31,11 @@ import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DataClassDescriptorResolver
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.findOriginalTopMostOverriddenDescriptors
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import java.util.*
|
||||
|
||||
fun PsiNamedElement.getAccessorNames(readable: Boolean = true, writable: Boolean = true): List<String> {
|
||||
@@ -87,3 +92,26 @@ fun KtParameter.isDataClassProperty(): Boolean {
|
||||
return this.containingClassOrObject?.hasModifier(KtTokens.DATA_KEYWORD) ?: false
|
||||
}
|
||||
|
||||
fun getTopMostOverriddenElementsToHighlight(target: PsiElement): List<PsiElement> {
|
||||
val callableDescriptor = (target as? KtCallableDeclaration)?.resolveToDescriptorIfAny() as? CallableDescriptor
|
||||
val descriptorsToHighlight = if (callableDescriptor is ParameterDescriptor)
|
||||
listOf(callableDescriptor)
|
||||
else
|
||||
callableDescriptor?.findOriginalTopMostOverriddenDescriptors() ?: emptyList()
|
||||
|
||||
return descriptorsToHighlight.mapNotNull { it.source.getPsi() }.filter { it != target }
|
||||
}
|
||||
|
||||
val KtDeclaration.descriptor: DeclarationDescriptor?
|
||||
get() = if (this is KtParameter) this.descriptor else this.resolveToDescriptorIfAny(BodyResolveMode.FULL)
|
||||
|
||||
val KtParameter.descriptor: ValueParameterDescriptor?
|
||||
get() = this.resolveToParameterDescriptorIfAny(BodyResolveMode.FULL)
|
||||
|
||||
fun isCallReceiverRefersToCompanionObject(element: KtElement, companionObject: KtObjectDeclaration): Boolean {
|
||||
val companionObjectDescriptor = companionObject.descriptor
|
||||
val bindingContext = element.analyze()
|
||||
val resolvedCall = bindingContext[BindingContext.CALL, element]?.getResolvedCall(bindingContext) ?: return false
|
||||
return (resolvedCall.dispatchReceiver as? ImplicitClassReceiver)?.declarationDescriptor == companionObjectDescriptor ||
|
||||
(resolvedCall.extensionReceiver as? ImplicitClassReceiver)?.declarationDescriptor == companionObjectDescriptor
|
||||
}
|
||||
|
||||
@@ -1,309 +0,0 @@
|
||||
/*
|
||||
* 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.search.usagesSearch
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.psi.search.searches.MethodReferencesSearch
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import com.intellij.psi.util.MethodSignatureUtil
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
||||
import org.jetbrains.kotlin.asJava.classes.lazyPub
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightElement
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
|
||||
import org.jetbrains.kotlin.asJava.toLightMethods
|
||||
import org.jetbrains.kotlin.asJava.unwrapped
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToParameterDescriptorIfAny
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.util.getJavaMethodDescriptor
|
||||
import org.jetbrains.kotlin.idea.references.unwrappedTargets
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.contains
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isTypeRefinementEnabled
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
|
||||
val KtDeclaration.descriptor: DeclarationDescriptor?
|
||||
get() = if (this is KtParameter) this.descriptor else this.resolveToDescriptorIfAny(BodyResolveMode.FULL)
|
||||
|
||||
val KtDeclaration.constructor: ConstructorDescriptor?
|
||||
get() {
|
||||
val context = this.analyze()
|
||||
return when (this) {
|
||||
is KtClassOrObject -> context[BindingContext.CLASS, this]?.unsubstitutedPrimaryConstructor
|
||||
is KtFunction -> context[BindingContext.CONSTRUCTOR, this]
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
val KtParameter.descriptor: ValueParameterDescriptor?
|
||||
get() = this.resolveToParameterDescriptorIfAny(BodyResolveMode.FULL)
|
||||
|
||||
val KtParameter.propertyDescriptor: PropertyDescriptor?
|
||||
get() = this.resolveToDescriptorIfAny(BodyResolveMode.FULL) as? PropertyDescriptor
|
||||
|
||||
fun PsiReference.checkUsageVsOriginalDescriptor(
|
||||
targetDescriptor: DeclarationDescriptor,
|
||||
declarationToDescriptor: (KtDeclaration) -> DeclarationDescriptor? = { it.descriptor },
|
||||
checker: (usageDescriptor: DeclarationDescriptor, targetDescriptor: DeclarationDescriptor) -> Boolean
|
||||
): Boolean {
|
||||
return unwrappedTargets
|
||||
.filterIsInstance<KtDeclaration>()
|
||||
.any {
|
||||
val usageDescriptor = declarationToDescriptor(it)
|
||||
usageDescriptor != null && checker(usageDescriptor, targetDescriptor)
|
||||
}
|
||||
}
|
||||
|
||||
fun PsiReference.isImportUsage(): Boolean =
|
||||
element.getNonStrictParentOfType<KtImportDirective>() != null
|
||||
|
||||
fun PsiReference.isConstructorUsage(ktClassOrObject: KtClassOrObject): Boolean = with(element) {
|
||||
fun checkJavaUsage(): Boolean {
|
||||
val call = getNonStrictParentOfType<PsiConstructorCall>()
|
||||
return call == parent && call?.resolveConstructor()?.containingClass?.navigationElement == ktClassOrObject
|
||||
}
|
||||
|
||||
fun checkKotlinUsage(): Boolean {
|
||||
if (this !is KtElement) return false
|
||||
|
||||
val descriptor = getConstructorCallDescriptor() as? ConstructorDescriptor ?: return false
|
||||
|
||||
val declaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor.containingDeclaration)
|
||||
return declaration == ktClassOrObject || (declaration is KtConstructor<*> && declaration.getContainingClassOrObject() == ktClassOrObject)
|
||||
}
|
||||
|
||||
checkJavaUsage() || checkKotlinUsage()
|
||||
}
|
||||
|
||||
private fun KtElement.getConstructorCallDescriptor(): DeclarationDescriptor? {
|
||||
val bindingContext = this.analyze()
|
||||
val constructorCalleeExpression = getNonStrictParentOfType<KtConstructorCalleeExpression>()
|
||||
if (constructorCalleeExpression != null) {
|
||||
return bindingContext.get(BindingContext.REFERENCE_TARGET, constructorCalleeExpression.constructorReferenceExpression)
|
||||
}
|
||||
|
||||
val callExpression = getNonStrictParentOfType<KtCallElement>()
|
||||
if (callExpression != null) {
|
||||
val callee = callExpression.calleeExpression
|
||||
if (callee is KtReferenceExpression) {
|
||||
return bindingContext.get(BindingContext.REFERENCE_TARGET, callee)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun PsiElement.processDelegationCallConstructorUsages(scope: SearchScope, process: (KtCallElement) -> Boolean): Boolean {
|
||||
val task = buildProcessDelegationCallConstructorUsagesTask(scope, process)
|
||||
return task()
|
||||
}
|
||||
|
||||
// should be executed under read-action, returns long-running part to be executed outside read-action
|
||||
fun PsiElement.buildProcessDelegationCallConstructorUsagesTask(scope: SearchScope, process: (KtCallElement) -> Boolean): () -> Boolean {
|
||||
ApplicationManager.getApplication().assertReadAccessAllowed()
|
||||
val task1 = buildProcessDelegationCallKotlinConstructorUsagesTask(scope, process)
|
||||
val task2 = buildProcessDelegationCallJavaConstructorUsagesTask(scope, process)
|
||||
return { task1() && task2() }
|
||||
}
|
||||
|
||||
private fun PsiElement.buildProcessDelegationCallKotlinConstructorUsagesTask(
|
||||
scope: SearchScope,
|
||||
process: (KtCallElement) -> Boolean
|
||||
): () -> Boolean {
|
||||
val element = unwrapped
|
||||
if (element != null && element !in scope) return { true }
|
||||
|
||||
val klass = when (element) {
|
||||
is KtConstructor<*> -> element.getContainingClassOrObject()
|
||||
is KtClass -> element
|
||||
else -> return { true }
|
||||
}
|
||||
|
||||
if (klass !is KtClass || element !is KtDeclaration) return { true }
|
||||
val descriptor = lazyPub { element.constructor }
|
||||
|
||||
if (!processClassDelegationCallsToSpecifiedConstructor(klass, descriptor, process)) return { false }
|
||||
|
||||
// long-running task, return it to execute outside read-action
|
||||
return { processInheritorsDelegatingCallToSpecifiedConstructor(klass, scope, descriptor, process) }
|
||||
}
|
||||
|
||||
private fun PsiElement.buildProcessDelegationCallJavaConstructorUsagesTask(
|
||||
scope: SearchScope,
|
||||
process: (KtCallElement) -> Boolean
|
||||
): () -> Boolean {
|
||||
if (this is KtLightElement<*, *>) return { true }
|
||||
// TODO: Temporary hack to avoid NPE while KotlinNoOriginLightMethod is around
|
||||
if (this is KtLightMethod && this.kotlinOrigin == null) return { true }
|
||||
if (!(this is PsiMethod && isConstructor)) return { true }
|
||||
val klass = containingClass ?: return { true }
|
||||
val descriptor = lazyPub { getJavaMethodDescriptor() as? ConstructorDescriptor }
|
||||
return { processInheritorsDelegatingCallToSpecifiedConstructor(klass, scope, descriptor, process) }
|
||||
}
|
||||
|
||||
|
||||
private fun processInheritorsDelegatingCallToSpecifiedConstructor(
|
||||
klass: PsiElement,
|
||||
scope: SearchScope,
|
||||
lazyDescriptor: Lazy<ConstructorDescriptor?>,
|
||||
process: (KtCallElement) -> Boolean
|
||||
): Boolean {
|
||||
return HierarchySearchRequest(klass, scope, false).searchInheritors().all {
|
||||
runReadAction {
|
||||
val unwrapped = it.takeIf { it.isValid }?.unwrapped
|
||||
if (unwrapped is KtClass)
|
||||
processClassDelegationCallsToSpecifiedConstructor(unwrapped, lazyDescriptor, process)
|
||||
else
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun processClassDelegationCallsToSpecifiedConstructor(
|
||||
klass: KtClass,
|
||||
lazyDescriptor: Lazy<ConstructorDescriptor?>,
|
||||
process: (KtCallElement) -> Boolean
|
||||
): Boolean {
|
||||
for (secondaryConstructor in klass.secondaryConstructors) {
|
||||
val delegationCallDescriptor =
|
||||
secondaryConstructor.getDelegationCall().getConstructorCallDescriptor()
|
||||
?: continue
|
||||
|
||||
if (lazyDescriptor.value == delegationCallDescriptor) {
|
||||
if (!process(secondaryConstructor.getDelegationCall())) return false
|
||||
}
|
||||
}
|
||||
if (!klass.isEnum()) return true
|
||||
for (declaration in klass.declarations) {
|
||||
if (declaration is KtEnumEntry) {
|
||||
val delegationCall =
|
||||
declaration.superTypeListEntries.firstOrNull() as? KtSuperTypeCallEntry
|
||||
?: continue
|
||||
val constructorCallDescriptor =
|
||||
delegationCall.calleeExpression.getConstructorCallDescriptor()
|
||||
?: continue
|
||||
|
||||
if (lazyDescriptor.value == constructorCallDescriptor) {
|
||||
if (!process(delegationCall)) return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if reference resolves to extension function whose receiver is the same as declaration's parent (or its superclass)
|
||||
// Used in extension search
|
||||
fun PsiReference.isExtensionOfDeclarationClassUsage(declaration: KtNamedDeclaration): Boolean {
|
||||
val descriptor = declaration.descriptor ?: return false
|
||||
return checkUsageVsOriginalDescriptor(descriptor) { usageDescriptor, targetDescriptor ->
|
||||
when (usageDescriptor) {
|
||||
targetDescriptor -> false
|
||||
!is FunctionDescriptor -> false
|
||||
else -> {
|
||||
val receiverDescriptor =
|
||||
usageDescriptor.extensionReceiverParameter?.type?.constructor?.declarationDescriptor
|
||||
val containingDescriptor = targetDescriptor.containingDeclaration
|
||||
|
||||
containingDescriptor == receiverDescriptor
|
||||
|| (containingDescriptor is ClassDescriptor
|
||||
&& receiverDescriptor is ClassDescriptor
|
||||
&& DescriptorUtils.isSubclass(containingDescriptor, receiverDescriptor))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if reference resolves to the declaration with the same parent
|
||||
// Used in overload search
|
||||
fun PsiReference.isUsageInContainingDeclaration(declaration: KtNamedDeclaration): Boolean {
|
||||
val descriptor = declaration.descriptor ?: return false
|
||||
return checkUsageVsOriginalDescriptor(descriptor) { usageDescriptor, targetDescriptor ->
|
||||
usageDescriptor != targetDescriptor
|
||||
&& usageDescriptor.containingDeclaration == targetDescriptor.containingDeclaration
|
||||
}
|
||||
}
|
||||
|
||||
fun PsiReference.isCallableOverrideUsage(declaration: KtNamedDeclaration): Boolean {
|
||||
val toDescriptor: (KtDeclaration) -> CallableDescriptor? = { sourceDeclaration ->
|
||||
if (sourceDeclaration is KtParameter) {
|
||||
// we don't treat parameters in overriding method as "override" here (overriding parameters usages are searched optionally and via searching of overriding methods first)
|
||||
if (sourceDeclaration.hasValOrVar()) sourceDeclaration.propertyDescriptor else null
|
||||
} else {
|
||||
sourceDeclaration.descriptor as? CallableDescriptor
|
||||
}
|
||||
}
|
||||
|
||||
val targetDescriptor = toDescriptor(declaration) ?: return false
|
||||
|
||||
return unwrappedTargets.any {
|
||||
when (it) {
|
||||
is KtDeclaration -> {
|
||||
val usageDescriptor = toDescriptor(it)
|
||||
usageDescriptor != null && OverridingUtil.overrides(
|
||||
usageDescriptor,
|
||||
targetDescriptor,
|
||||
usageDescriptor.module.isTypeRefinementEnabled(),
|
||||
false // don't distinguish between expect and non-expect callable descriptors, KT-38298, KT-38589
|
||||
)
|
||||
}
|
||||
is PsiMethod -> {
|
||||
declaration.toLightMethods().any { superMethod -> MethodSignatureUtil.isSuperMethod(superMethod, it) }
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun PsiElement.searchReferencesOrMethodReferences(): Collection<PsiReference> {
|
||||
val lightMethods = toLightMethods()
|
||||
return if (lightMethods.isNotEmpty()) {
|
||||
lightMethods.flatMapTo(LinkedHashSet()) { MethodReferencesSearch.search(it) }
|
||||
} else {
|
||||
ReferencesSearch.search(this).findAll()
|
||||
}
|
||||
}
|
||||
|
||||
fun <T : PsiNamedElement> List<T>.filterDataClassComponentsIfDisabled(kotlinOptions: KotlinReferencesSearchOptions): List<T> {
|
||||
if (kotlinOptions.searchForComponentConventions) return this
|
||||
|
||||
fun PsiNamedElement.isComponentElement(): Boolean {
|
||||
|
||||
if (this !is PsiMethod) return false
|
||||
|
||||
val dataClassParent = ((parent as? KtLightClass)?.kotlinOrigin as? KtClass)?.isData() == true
|
||||
if (!dataClassParent) return false
|
||||
|
||||
if (!Name.isValidIdentifier(name)) return false
|
||||
val nameIdentifier = Name.identifier(name)
|
||||
if (!DataClassDescriptorResolver.isComponentLike(nameIdentifier)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return filter { !it.isComponentElement() }
|
||||
}
|
||||
Reference in New Issue
Block a user