diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetParameter.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetParameter.java index c02ee94de2f..37f056ca273 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetParameter.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetParameter.java @@ -179,13 +179,24 @@ public class JetParameter extends JetNamedDeclarationStub i @NotNull @Override public SearchScope getUseScope() { - PsiElement parent = getParent(); - if (parent != null) { - PsiElement grandparent = parent.getParent(); - if (grandparent instanceof JetFunction && !(grandparent instanceof JetFunctionLiteral)) { - return grandparent.getUseScope(); - } + JetDeclaration function = getOwnerFunction(); + if (function != null && !(function instanceof JetFunctionLiteral)) { + return function.getUseScope(); } return super.getUseScope(); } + + @NotNull + public SearchScope getUseScopeExceptNamedArguments() { + return super.getUseScope(); + } + + @Nullable + public JetFunction getOwnerFunction() { + PsiElement parent = getParent(); + if (parent == null) return null; + PsiElement grandparent = parent.getParent(); + if (!(grandparent instanceof JetFunction)) return null; + return (JetFunction) grandparent; + } } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/KotlinIndexers.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/KotlinIndexers.kt index 20829945b29..8fbcd59630d 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/KotlinIndexers.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/KotlinIndexers.kt @@ -32,7 +32,9 @@ import org.jetbrains.kotlin.lexer.JetLexer import org.jetbrains.kotlin.lexer.JetTokens import java.util.ArrayDeque -class KotlinFilterLexer(table: OccurrenceConsumer): BaseFilterLexer(JetLexer(), table) { +val KOTLIN_NAMED_ARGUMENT_SEARCH_CONTEXT: Short = 0x20 + +class KotlinFilterLexer(private val occurrenceConsumer: OccurrenceConsumer): BaseFilterLexer(JetLexer(), occurrenceConsumer) { private val codeTokens = TokenSet.orSet( TokenSet.create(*ALL_SEARCHABLE_OPERATIONS.toTypedArray()), TokenSet.create(JetTokens.IDENTIFIER, JetTokens.FIELD_IDENTIFIER) @@ -46,15 +48,30 @@ class KotlinFilterLexer(table: OccurrenceConsumer): BaseFilterLexer(JetLexer(), ) private val MAX_PREV_TOKENS = 2 - private val previousTokens = ArrayDeque(MAX_PREV_TOKENS) + private val prevTokens = ArrayDeque(MAX_PREV_TOKENS) + private var prevTokenStart = -1 + private var prevTokenEnd = -1 override fun advance() { val tokenType = myDelegate.tokenType when (tokenType) { - in codeTokens -> addOccurrenceInToken(UsageSearchContext.IN_CODE.toInt()) + JetTokens.EQ -> { + if (prevTokens.peekFirst() == JetTokens.IDENTIFIER) { + val prevPrev = prevTokens.elementAtOrNull(1) + if (prevPrev == JetTokens.COMMA || prevPrev == JetTokens.LPAR) { + occurrenceConsumer.addOccurrence(bufferSequence, null, prevTokenStart, prevTokenEnd, KOTLIN_NAMED_ARGUMENT_SEARCH_CONTEXT.toInt()) + } + } + } - JetTokens.LPAR -> if (isMultiDeclarationPosition()) addOccurrenceInToken(UsageSearchContext.IN_CODE.toInt()) + JetTokens.LPAR -> { + if (isMultiDeclarationPosition()) { + addOccurrenceInToken(UsageSearchContext.IN_CODE.toInt()) + } + } + + in codeTokens -> addOccurrenceInToken(UsageSearchContext.IN_CODE.toInt()) in JetTokens.STRINGS -> scanWordsInToken(UsageSearchContext.IN_STRINGS + UsageSearchContext.IN_FOREIGN_LANGUAGES, false, true) @@ -66,26 +83,29 @@ class KotlinFilterLexer(table: OccurrenceConsumer): BaseFilterLexer(JetLexer(), !in skipTokens -> scanWordsInToken(UsageSearchContext.IN_PLAIN_TEXT.toInt(), false, false) } - if (tokenType != TokenType.WHITE_SPACE) { - if (previousTokens.size() == MAX_PREV_TOKENS) { - previousTokens.removeLast() + if (tokenType != TokenType.WHITE_SPACE && tokenType !in commentTokens) { + if (prevTokens.size() == MAX_PREV_TOKENS) { + prevTokens.removeLast() } - previousTokens.addFirst(tokenType) - + prevTokens.addFirst(tokenType) + prevTokenStart = tokenStart + prevTokenEnd = tokenEnd } myDelegate.advance() } private fun isMultiDeclarationPosition(): Boolean { - val first = previousTokens.peekFirst() + val first = prevTokens.peekFirst() if (first == JetTokens.VAL_KEYWORD || first == JetTokens.VAR_KEYWORD) return true - return first == JetTokens.LPAR && previousTokens.elementAtOrNull(1) == JetTokens.FOR_KEYWORD + return first == JetTokens.LPAR && prevTokens.elementAtOrNull(1) == JetTokens.FOR_KEYWORD } } class KotlinIdIndexer: LexerBasedIdIndexer() { override fun createLexer(consumer: OccurrenceConsumer): Lexer = KotlinFilterLexer(consumer) + + override fun getVersion() = 2 } class KotlinTodoIndexer: LexerBasedTodoIndexer(), IdAndToDoScannerBasedOnFilterLexer { diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/searchHelpers.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/searchHelpers.kt index e215a6c39bd..9a3f292c663 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/searchHelpers.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/searchHelpers.kt @@ -18,15 +18,21 @@ package org.jetbrains.kotlin.idea.search.usagesSearch import com.intellij.psi.PsiNamedElement import com.intellij.psi.PsiReference +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.search.UsageSearchContext import com.intellij.util.Processor import com.intellij.util.containers.ContainerUtil import org.jetbrains.kotlin.asJava.LightClassUtil import org.jetbrains.kotlin.asJava.LightClassUtil.PropertyAccessorsPsiMethods import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.references.JetSimpleNameReference import org.jetbrains.kotlin.idea.references.matchesTarget +import org.jetbrains.kotlin.idea.search.KOTLIN_NAMED_ARGUMENT_SEARCH_CONTEXT +import org.jetbrains.kotlin.idea.search.and import org.jetbrains.kotlin.idea.search.usagesSearch.UsagesSearchFilter.False import org.jetbrains.kotlin.idea.search.usagesSearch.UsagesSearchFilter.True +import org.jetbrains.kotlin.idea.stubindex.JetSourceFilterScope import org.jetbrains.kotlin.lexer.JetSingleValueToken import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.name.Name @@ -196,7 +202,7 @@ class ClassDeclarationsUsagesSearchHelper( for (decl in target.element.effectiveDeclarations()) { if ((decl is JetNamedFunction && functionUsages) || ((decl is JetProperty || decl is JetParameter) && propertyUsages)) { - items.add(declHelper.newItem(target.retarget(decl as JetNamedDeclaration))) + items.add(declHelper.newItem(target.withTarget(decl as JetNamedDeclaration))) } } @@ -237,10 +243,46 @@ val isPropertyReadOnlyUsage = (PsiReference::isPropertyReadOnlyUsage).searchFilt class PropertyUsagesSearchHelper( public val readUsages: Boolean = true, public val writeUsages: Boolean = true, + public val namedArgumentUsages: Boolean = true, public override val selfUsages: Boolean = true, public override val overrideUsages: Boolean = true, skipImports: Boolean = false ) : DefaultSearchHelper(skipImports), OverrideSearchHelper { + override fun makeItemList(target: UsagesSearchTarget): List { + val element = target.element + if (element is JetParameter) { + val realUsagesScope = element.useScopeExceptNamedArguments + val realUsagesTarget = target.withScope(realUsagesScope and target.effectiveScope) + val realUsagesFilter = makeFilter(target) and !(PsiReference::isNamedArgumentUsage).searchFilter + val realUsagesRequest = UsagesSearchRequestItem(realUsagesTarget, makeWordList(target), realUsagesFilter, makeAdditionalSearchDelegate(target.element)) + + if (!namedArgumentUsages) { + return listOf(realUsagesRequest) + } + + val function = element.ownerFunction + ?: return listOf(realUsagesRequest) // parameter in for or something like that + if (function.nameAsName?.isSpecial ?: true) { // function literal or anonymous function + return listOf(realUsagesRequest) + } + + var scope = target.effectiveScope + if (scope is GlobalSearchScope) { + scope = JetSourceFilterScope.kotlinSources(scope, element.project) + } + + val additionalFilter1 = AdditionalFileFilter(element.name!!, KOTLIN_NAMED_ARGUMENT_SEARCH_CONTEXT, true) + val additionalFilter2 = AdditionalFileFilter(function.name!!, UsageSearchContext.IN_CODE, true) + val namedArgsRequest = UsagesSearchRequestItem(target.withScope(scope), + listOf(element.name!!), + (PsiReference::isNamedArgumentUsage).searchFilter, + additionalFileFilters = listOf(additionalFilter1, additionalFilter2)) + return listOf(realUsagesRequest, namedArgsRequest) + } + + return super.makeItemList(target) + } + override fun makeWordList(target: UsagesSearchTarget): List { return with(target.element) { ContainerUtil.createMaybeSingletonList(getName()) + @@ -250,12 +292,26 @@ class PropertyUsagesSearchHelper( } override fun makeFilter(target: UsagesSearchTarget): UsagesSearchFilter { - val readWriteUsage = when { + val readWriteFilter = when { readUsages && writeUsages -> True readUsages -> isPropertyReadOnlyUsage writeUsages -> !isPropertyReadOnlyUsage else -> False } - return isTargetOrOverrideUsage and readWriteUsage and isFilteredImport + var result = isTargetOrOverrideUsage and readWriteFilter and isFilteredImport + if (!namedArgumentUsages) { + result = result and !(PsiReference::isNamedArgumentUsage).searchFilter + } + return result + } + + companion object { } } + +private fun PsiReference.isNamedArgumentUsage(): Boolean { + if (this !is JetSimpleNameReference) return false + return expression.parent is JetValueArgumentName +} + + diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/usagesSearch.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/usagesSearch.kt index 02e034d2e5f..a17a7f28cbc 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/usagesSearch.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/usagesSearch.kt @@ -16,40 +16,25 @@ package org.jetbrains.kotlin.idea.search.usagesSearch -import com.intellij.psi.PsiReference -import com.intellij.util.QueryFactory -import com.intellij.psi.search.SearchScope -import com.intellij.psi.search.SearchRequestCollector -import com.intellij.psi.search.SearchSession -import com.intellij.psi.search.PsiSearchHelper -import com.intellij.util.Query -import com.intellij.util.MergeQuery -import com.intellij.psi.search.SearchRequestQuery -import com.intellij.util.UniqueResultsQuery -import com.intellij.psi.search.searches.ReferenceDescriptor -import com.intellij.util.containers.ContainerUtil -import com.intellij.psi.search.UsageSearchContext -import com.intellij.psi.PsiElement -import com.intellij.util.Processor import com.intellij.openapi.application.QueryExecutorBase -import com.intellij.psi.PsiReferenceService import com.intellij.openapi.progress.ProgressManager -import com.intellij.psi.ReferenceRange import com.intellij.openapi.project.Project -import org.jetbrains.kotlin.idea.search.usagesSearch.UsagesSearchFilter.* -import org.jetbrains.kotlin.idea.search.and -import com.intellij.psi.impl.search.PsiSearchHelperImpl -import com.intellij.psi.search.GlobalSearchScope -import com.intellij.openapi.vfs.VirtualFile -import com.intellij.psi.impl.cache.impl.id.IdIndexEntry -import java.util.Collections import com.intellij.openapi.roots.FileIndexFacade -import com.intellij.util.indexing.FileBasedIndex -import com.intellij.psi.impl.cache.impl.id.IdIndex -import org.jetbrains.kotlin.idea.util.application.runReadAction -import com.intellij.psi.search.TextOccurenceProcessor -import com.intellij.psi.PsiManager +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.* import com.intellij.psi.impl.PsiManagerEx +import com.intellij.psi.impl.cache.impl.id.IdIndex +import com.intellij.psi.impl.cache.impl.id.IdIndexEntry +import com.intellij.psi.impl.search.PsiSearchHelperImpl +import com.intellij.psi.search.* +import com.intellij.psi.search.searches.ReferenceDescriptor +import com.intellij.util.* +import com.intellij.util.containers.ContainerUtil +import com.intellij.util.indexing.FileBasedIndex +import org.jetbrains.kotlin.idea.search.and +import org.jetbrains.kotlin.idea.search.usagesSearch.UsagesSearchFilter.False +import org.jetbrains.kotlin.idea.util.application.runReadAction +import java.util.Collections public data class UsagesSearchLocation( val inCode: Boolean = true, @@ -81,7 +66,10 @@ public data class UsagesSearchTarget( val location: UsagesSearchLocation = UsagesSearchLocation.DEFAULT, val restrictByTargetScope: Boolean = true ) { - fun retarget(element: U) = + fun withTarget(element: U) = + UsagesSearchTarget(element, scope, location, restrictByTargetScope) + + fun withScope(scope: SearchScope) = UsagesSearchTarget(element, scope, location, restrictByTargetScope) val effectiveScope: SearchScope @@ -100,11 +88,18 @@ public interface UsagesSearchFilter { fun accepts(ref: PsiReference, item: UsagesSearchRequestItem): Boolean } -public data class UsagesSearchRequestItem( +public class UsagesSearchRequestItem( val target: UsagesSearchTarget, val words: List, val filter: UsagesSearchFilter, - val additionalSearchDelegate: ((Processor) -> Boolean)? + val additionalSearchDelegate: ((Processor) -> Boolean)? = null, + val additionalFileFilters: Collection = emptyList() +) + +public data class AdditionalFileFilter( + val word: String, + val searchContext: Short, + val caseSensitively: Boolean ) public data class UsagesSearchRequest(val project: Project, val items: List) { @@ -153,7 +148,16 @@ public class KotlinPsiSearchHelper(private val project: Project): PsiSearchHelpe } public fun processFilesWithText(item: UsagesSearchRequestItem, consumer: Processor): Boolean { - val scope = runReadAction { item.target.effectiveScope } + var scope = runReadAction { item.target.effectiveScope } + + for (filter in item.additionalFileFilters) { + if (scope !is GlobalSearchScope) break + val processor = CommonProcessors.CollectProcessor() + processFilesWithText(scope, filter.searchContext, filter.caseSensitively, filter.word, processor) + if (processor.results.isEmpty()) return true + scope = GlobalSearchScope.filesScope(project, processor.results) + } + return item.words.all { word -> val textProcessor = ResultTextProcessorImpl(item, consumer) processElementsWithWord(textProcessor, scope, word, item.target.location.searchContext, true) diff --git a/idea/src/org/jetbrains/kotlin/idea/findUsages/findUsagesOptions.kt b/idea/src/org/jetbrains/kotlin/idea/findUsages/findUsagesOptions.kt index f1286b7cc98..db51ce5a52e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/findUsages/findUsagesOptions.kt +++ b/idea/src/org/jetbrains/kotlin/idea/findUsages/findUsagesOptions.kt @@ -16,19 +16,14 @@ package org.jetbrains.kotlin.idea.findUsages -import com.intellij.find.findUsages.JavaMethodFindUsagesOptions -import com.intellij.openapi.project.Project -import com.intellij.find.findUsages.JavaClassFindUsagesOptions -import org.jetbrains.kotlin.idea.search.usagesSearch.ClassUsagesSearchHelper -import org.jetbrains.kotlin.idea.search.usagesSearch.UsagesSearchTarget -import com.intellij.psi.PsiNamedElement -import org.jetbrains.kotlin.idea.search.usagesSearch.UsagesSearchLocation import com.intellij.find.findUsages.FindUsagesOptions -import org.jetbrains.kotlin.idea.search.usagesSearch.PropertyUsagesSearchHelper -import com.intellij.psi.search.GlobalSearchScope -import org.jetbrains.kotlin.idea.search.usagesSearch.ClassDeclarationsUsagesSearchHelper -import org.jetbrains.kotlin.idea.search.usagesSearch.FunctionUsagesSearchHelper +import com.intellij.find.findUsages.JavaClassFindUsagesOptions +import com.intellij.find.findUsages.JavaMethodFindUsagesOptions import com.intellij.find.findUsages.JavaVariableFindUsagesOptions +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiNamedElement +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.kotlin.idea.search.usagesSearch.* public class KotlinClassFindUsagesOptions(project: Project) : JavaClassFindUsagesOptions(project) { public var searchConstructorUsages: Boolean = true @@ -101,6 +96,7 @@ fun KotlinPropertyFindUsagesOptions.toHelper(): PropertyUsagesSearchHelper = PropertyUsagesSearchHelper( selfUsages = isUsages, overrideUsages = isUsages, + namedArgumentUsages = isUsages, readUsages = isReadAccess, writeUsages = isWriteAccess, skipImports = isSkipImportStatements diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt index 4aa5f387255..edbad823697 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt @@ -40,7 +40,6 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotated import org.jetbrains.kotlin.idea.JetBundle import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinFindClassUsagesHandler -import org.jetbrains.kotlin.idea.project.ProjectStructureUtil import org.jetbrains.kotlin.idea.search.usagesSearch.* import org.jetbrains.kotlin.idea.util.ProjectRootsUtil import org.jetbrains.kotlin.lexer.JetTokens @@ -220,7 +219,7 @@ public class UnusedSymbolInspection : AbstractKotlinInspection() { val searchHelper: UsagesSearchHelper = when (declaration) { is JetClassOrObject -> ClassUsagesSearchHelper(constructorUsages = true, nonConstructorUsages = true, skipImports = true) is JetNamedFunction -> FunctionUsagesSearchHelper(skipImports = true) - is JetProperty, is JetParameter -> PropertyUsagesSearchHelper(skipImports = true) + is JetProperty, is JetParameter -> PropertyUsagesSearchHelper(skipImports = true, namedArgumentUsages = false) else -> DefaultSearchHelper() } diff --git a/idea/testData/findUsages/kotlin/findPropertyUsages/kotlinPrivatePropertyUsages2.results.txt b/idea/testData/findUsages/kotlin/findPropertyUsages/kotlinPrivatePropertyUsages2.results.txt index 4e7b35d3730..8ddf68005f4 100644 --- a/idea/testData/findUsages/kotlin/findPropertyUsages/kotlinPrivatePropertyUsages2.results.txt +++ b/idea/testData/findUsages/kotlin/findPropertyUsages/kotlinPrivatePropertyUsages2.results.txt @@ -1,5 +1,3 @@ [kotlinPrivatePropertyUsages2.0.kt] Named argument (9: 33) public class ServerEx(): Server(foo = "!") { -[kotlinPrivatePropertyUsages2.0.kt] Value read (10: 45) override fun processRequest() = "foo" + foo // this reference is found as a side effect of big use scope of constructor parameter: [kotlinPrivatePropertyUsages2.0.kt] Value read (6: 33) open fun processRequest() = foo -[kotlinPrivatePropertyUsages2.1.kt] Named argument (5: 24) println(Server(foo = "!").foo) -[kotlinPrivatePropertyUsages2.1.kt] Value read (5: 35) println(Server(foo = "!").foo) +[kotlinPrivatePropertyUsages2.1.kt] Named argument (5: 24) println(Server(foo = "!").foo) \ No newline at end of file