From 6d4a045cdfe19d3a4ecf1e335beb3c43682c7283 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Wed, 19 Nov 2014 18:41:58 +0300 Subject: [PATCH] Partial body resolve: to take source Kotlin callables with Nothing type from caches GitOrigin-RevId: abde9b141fab052dab0fe0ac6961a7a5fdcaf764 --- .../plugin/project/ResolveElementCache.java | 35 +++++++++++++ ...PossiblyNothingFunctionShortNameIndex.java | 50 +++++++++++++++++++ ...PossiblyNothingPropertyShortNameIndex.java | 50 +++++++++++++++++++ .../stubindex/StubIndexServiceImpl.java | 8 +++ .../lang/resolve/lazy/ElementResolver.java | 7 ++- .../resolve/lazy/PartialBodyResolveFilter.kt | 19 +++++-- .../PossiblyNothingCallableNamesService.kt | 29 +++++++++++ plugins/kotlin/idea/src/META-INF/plugin.xml | 2 + .../PartialBodyResolveTestGenerated.java | 19 +++++++ .../IfNotIsErrorVariable.dump | 3 ++ .../IfNotIsErrorVariable.kt | 7 +++ .../partialBodyResolve/IfNotIsMyError.dump | 2 + .../partialBodyResolve/IfNotIsMyError.kt | 8 +++ .../IfNotIsNothingProp.dump | 2 + .../partialBodyResolve/IfNotIsNothingProp.kt | 9 ++++ 15 files changed, 244 insertions(+), 6 deletions(-) create mode 100644 plugins/kotlin/analysis/src/org/jetbrains/jet/plugin/stubindex/JetPossiblyNothingFunctionShortNameIndex.java create mode 100644 plugins/kotlin/analysis/src/org/jetbrains/jet/plugin/stubindex/JetPossiblyNothingPropertyShortNameIndex.java create mode 100644 plugins/kotlin/common/src/org/jetbrains/jet/lang/resolve/lazy/PossiblyNothingCallableNamesService.kt create mode 100644 plugins/kotlin/idea/tests/testData/resolve/partialBodyResolve/IfNotIsErrorVariable.dump create mode 100644 plugins/kotlin/idea/tests/testData/resolve/partialBodyResolve/IfNotIsErrorVariable.kt create mode 100644 plugins/kotlin/idea/tests/testData/resolve/partialBodyResolve/IfNotIsMyError.dump create mode 100644 plugins/kotlin/idea/tests/testData/resolve/partialBodyResolve/IfNotIsMyError.kt create mode 100644 plugins/kotlin/idea/tests/testData/resolve/partialBodyResolve/IfNotIsNothingProp.dump create mode 100644 plugins/kotlin/idea/tests/testData/resolve/partialBodyResolve/IfNotIsNothingProp.kt diff --git a/plugins/kotlin/analysis/src/org/jetbrains/jet/plugin/project/ResolveElementCache.java b/plugins/kotlin/analysis/src/org/jetbrains/jet/plugin/project/ResolveElementCache.java index 5275f51ca0e7..4bad72453518 100644 --- a/plugins/kotlin/analysis/src/org/jetbrains/jet/plugin/project/ResolveElementCache.java +++ b/plugins/kotlin/analysis/src/org/jetbrains/jet/plugin/project/ResolveElementCache.java @@ -28,16 +28,26 @@ import org.jetbrains.jet.lang.psi.JetElement; import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.resolve.AdditionalCheckerProvider; import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.lazy.DefaultNothingCallableNamesService; import org.jetbrains.jet.lang.resolve.lazy.ElementResolver; +import org.jetbrains.jet.lang.resolve.lazy.PossiblyNothingCallableNamesService; import org.jetbrains.jet.lang.resolve.lazy.ResolveSession; +import org.jetbrains.jet.plugin.stubindex.JetPossiblyNothingFunctionShortNameIndex; +import org.jetbrains.jet.plugin.stubindex.JetPossiblyNothingPropertyShortNameIndex; import org.jetbrains.jet.storage.LazyResolveStorageManager; import org.jetbrains.jet.storage.MemoizedFunctionToNotNull; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + public class ResolveElementCache extends ElementResolver { + private final Project project; private final CachedValue> additionalResolveCache; public ResolveElementCache(ResolveSession resolveSession, Project project) { super(resolveSession); + this.project = project; // Recreate internal cache after change of modification count this.additionalResolveCache = @@ -74,4 +84,29 @@ public class ResolveElementCache extends ElementResolver { public AdditionalCheckerProvider getAdditionalCheckerProvider(@NotNull JetFile jetFile) { return TargetPlatformDetector.getPlatform(jetFile).getAdditionalCheckerProvider(); } + + @NotNull + @Override + protected PossiblyNothingCallableNamesService possiblyNothingCallableNamesService() { + return new PossiblyNothingCallableNamesService() { + @NotNull + @Override + public Set functionNames() { + // we have to add hardcoded-names until we have Kotlin compiled classes in caches + Set hardcodedNames = DefaultNothingCallableNamesService.INSTANCE$.functionNames(); + Collection indexedNames = JetPossiblyNothingFunctionShortNameIndex.getInstance().getAllKeys(project); + Set set = new HashSet(hardcodedNames.size() + indexedNames.size()); + set.addAll(hardcodedNames); + set.addAll(indexedNames); + return set; + //TODO: what about local declarations? + } + + @NotNull + @Override + public Set propertyNames() { + return new HashSet(JetPossiblyNothingPropertyShortNameIndex.getInstance().getAllKeys(project)); + } + }; + } } diff --git a/plugins/kotlin/analysis/src/org/jetbrains/jet/plugin/stubindex/JetPossiblyNothingFunctionShortNameIndex.java b/plugins/kotlin/analysis/src/org/jetbrains/jet/plugin/stubindex/JetPossiblyNothingFunctionShortNameIndex.java new file mode 100644 index 000000000000..20e18532a5f9 --- /dev/null +++ b/plugins/kotlin/analysis/src/org/jetbrains/jet/plugin/stubindex/JetPossiblyNothingFunctionShortNameIndex.java @@ -0,0 +1,50 @@ +/* + * Copyright 2010-2014 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.jet.plugin.stubindex; + +import com.intellij.openapi.project.Project; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.stubs.StringStubIndexExtension; +import com.intellij.psi.stubs.StubIndexKey; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetNamedFunction; + +import java.util.Collection; + +public class JetPossiblyNothingFunctionShortNameIndex extends StringStubIndexExtension { + private static final StubIndexKey KEY = KotlinIndexUtil.createIndexKey(JetPossiblyNothingFunctionShortNameIndex.class); + + private static final JetPossiblyNothingFunctionShortNameIndex ourInstance = new JetPossiblyNothingFunctionShortNameIndex(); + + public static JetPossiblyNothingFunctionShortNameIndex getInstance() { + return ourInstance; + } + + private JetPossiblyNothingFunctionShortNameIndex() {} + + @NotNull + @Override + public StubIndexKey getKey() { + return KEY; + } + + @NotNull + @Override + public Collection get(@NotNull String s, @NotNull Project project, @NotNull GlobalSearchScope scope) { + return super.get(s, project, JetSourceFilterScope.kotlinSourcesAndLibraries(scope, project)); + } +} diff --git a/plugins/kotlin/analysis/src/org/jetbrains/jet/plugin/stubindex/JetPossiblyNothingPropertyShortNameIndex.java b/plugins/kotlin/analysis/src/org/jetbrains/jet/plugin/stubindex/JetPossiblyNothingPropertyShortNameIndex.java new file mode 100644 index 000000000000..d51bb4639d4b --- /dev/null +++ b/plugins/kotlin/analysis/src/org/jetbrains/jet/plugin/stubindex/JetPossiblyNothingPropertyShortNameIndex.java @@ -0,0 +1,50 @@ +/* + * Copyright 2010-2014 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.jet.plugin.stubindex; + +import com.intellij.openapi.project.Project; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.stubs.StringStubIndexExtension; +import com.intellij.psi.stubs.StubIndexKey; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetProperty; + +import java.util.Collection; + +public class JetPossiblyNothingPropertyShortNameIndex extends StringStubIndexExtension { + private static final StubIndexKey KEY = KotlinIndexUtil.createIndexKey(JetPossiblyNothingPropertyShortNameIndex.class); + + private static final JetPossiblyNothingPropertyShortNameIndex ourInstance = new JetPossiblyNothingPropertyShortNameIndex(); + + public static JetPossiblyNothingPropertyShortNameIndex getInstance() { + return ourInstance; + } + + private JetPossiblyNothingPropertyShortNameIndex() {} + + @NotNull + @Override + public StubIndexKey getKey() { + return KEY; + } + + @NotNull + @Override + public Collection get(@NotNull String s, @NotNull Project project, @NotNull GlobalSearchScope scope) { + return super.get(s, project, JetSourceFilterScope.kotlinSourcesAndLibraries(scope, project)); + } +} diff --git a/plugins/kotlin/analysis/src/org/jetbrains/jet/plugin/stubindex/StubIndexServiceImpl.java b/plugins/kotlin/analysis/src/org/jetbrains/jet/plugin/stubindex/StubIndexServiceImpl.java index 283823b5d202..549538e967c3 100644 --- a/plugins/kotlin/analysis/src/org/jetbrains/jet/plugin/stubindex/StubIndexServiceImpl.java +++ b/plugins/kotlin/analysis/src/org/jetbrains/jet/plugin/stubindex/StubIndexServiceImpl.java @@ -113,6 +113,10 @@ public class StubIndexServiceImpl implements StubIndexService { } } sink.occurrence(JetFunctionShortNameIndex.getInstance().getKey(), name); + + if (stub.isPossiblyNothingType()) { + sink.occurrence(JetPossiblyNothingFunctionShortNameIndex.getInstance().getKey(), name); + } } // can have special fq name in case of syntactically incorrect function with no name FqName topFQName = stub.getFqName(); @@ -136,6 +140,10 @@ public class StubIndexServiceImpl implements StubIndexService { } sink.occurrence(JetPropertyShortNameIndex.getInstance().getKey(), name); + + if (stub.isPossiblyNothingType()) { + sink.occurrence(JetPossiblyNothingPropertyShortNameIndex.getInstance().getKey(), name); + } } // can have special fq name in case of syntactically incorrect function with no name if (stub.isTopLevel()) { diff --git a/plugins/kotlin/common/src/org/jetbrains/jet/lang/resolve/lazy/ElementResolver.java b/plugins/kotlin/common/src/org/jetbrains/jet/lang/resolve/lazy/ElementResolver.java index c373b003c3a5..e4d508c9d69f 100644 --- a/plugins/kotlin/common/src/org/jetbrains/jet/lang/resolve/lazy/ElementResolver.java +++ b/plugins/kotlin/common/src/org/jetbrains/jet/lang/resolve/lazy/ElementResolver.java @@ -69,6 +69,11 @@ public abstract class ElementResolver { return resolveToElement(jetElement, false); } + @NotNull + protected PossiblyNothingCallableNamesService possiblyNothingCallableNamesService() { + return DefaultNothingCallableNamesService.INSTANCE$; + } + @NotNull public BindingContext resolveToElement(@NotNull JetElement jetElement, boolean partialBodyResolve) { @SuppressWarnings("unchecked") JetElement elementOfAdditionalResolve = (JetElement) JetPsiUtil.getTopmostParentOfTypes( @@ -96,7 +101,7 @@ public abstract class ElementResolver { boolean inBody = body != null && PsiTreeUtil.isAncestor(body, jetElement, false); Function1 filter; if (inBody) { - filter = new PartialBodyResolveFilter(jetElement, body); + filter = new PartialBodyResolveFilter(jetElement, body, possiblyNothingCallableNamesService()); } else { // do as less as possible body-resolve filter = new Function1() { diff --git a/plugins/kotlin/common/src/org/jetbrains/jet/lang/resolve/lazy/PartialBodyResolveFilter.kt b/plugins/kotlin/common/src/org/jetbrains/jet/lang/resolve/lazy/PartialBodyResolveFilter.kt index 1d9bca9220c1..16c4bb03cc69 100644 --- a/plugins/kotlin/common/src/org/jetbrains/jet/lang/resolve/lazy/PartialBodyResolveFilter.kt +++ b/plugins/kotlin/common/src/org/jetbrains/jet/lang/resolve/lazy/PartialBodyResolveFilter.kt @@ -27,10 +27,16 @@ import com.intellij.psi.PsiElement import org.jetbrains.jet.JetNodeTypes import org.jetbrains.jet.lang.psi.psiUtil.isAncestor -class PartialBodyResolveFilter(elementToResolve: JetElement, private val body: JetExpression) : (JetElement) -> Boolean { +class PartialBodyResolveFilter( + elementToResolve: JetElement, + private val body: JetExpression, + possiblyNothingCallableNamesService: PossiblyNothingCallableNamesService +) : (JetElement) -> Boolean { private val statementsToResolve = HashSet() private val processedBlocks = HashSet() + private val possiblyNothingFunctionNames = possiblyNothingCallableNamesService.functionNames() + private val possiblyNothingPropertyNames = possiblyNothingCallableNamesService.propertyNames() ;{ assert(body.isAncestor(elementToResolve, strict = false)) @@ -286,6 +292,13 @@ class PartialBodyResolveFilter(elementToResolve: JetElement, private val body: J super.visitCallExpression(expression) } + override fun visitSimpleNameExpression(expression: JetSimpleNameExpression) { + val name = expression.getReferencedName() + if (name in possiblyNothingPropertyNames) { + result.add(expression) + } + } + override fun visitBinaryExpression(expression: JetBinaryExpression) { if (expression.getOperationToken() == JetTokens.ELVIS) { // do not search exits after "?:" @@ -368,9 +381,5 @@ class PartialBodyResolveFilter(elementToResolve: JetElement, private val body: J private fun JetBlockExpression.lastStatement(): JetExpression? = getLastChild().siblings(forward = false).filterIsInstance().firstOrNull() - - class object { - private val possiblyNothingFunctionNames = setOf("error") // currently hard-coded - } } diff --git a/plugins/kotlin/common/src/org/jetbrains/jet/lang/resolve/lazy/PossiblyNothingCallableNamesService.kt b/plugins/kotlin/common/src/org/jetbrains/jet/lang/resolve/lazy/PossiblyNothingCallableNamesService.kt new file mode 100644 index 000000000000..7a4b5fb239df --- /dev/null +++ b/plugins/kotlin/common/src/org/jetbrains/jet/lang/resolve/lazy/PossiblyNothingCallableNamesService.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2010-2014 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.jet.lang.resolve.lazy + +public trait PossiblyNothingCallableNamesService { + public fun functionNames(): Set + public fun propertyNames(): Set +} + +public object DefaultNothingCallableNamesService : PossiblyNothingCallableNamesService { + private val hardcodedNames = setOf("error") + + override fun functionNames() = hardcodedNames + override fun propertyNames(): Set = setOf() +} \ No newline at end of file diff --git a/plugins/kotlin/idea/src/META-INF/plugin.xml b/plugins/kotlin/idea/src/META-INF/plugin.xml index d6630beaa943..cabfe4506f57 100644 --- a/plugins/kotlin/idea/src/META-INF/plugin.xml +++ b/plugins/kotlin/idea/src/META-INF/plugin.xml @@ -395,6 +395,8 @@ + + diff --git a/plugins/kotlin/idea/tests/test/org/jetbrains/jet/resolve/PartialBodyResolveTestGenerated.java b/plugins/kotlin/idea/tests/test/org/jetbrains/jet/resolve/PartialBodyResolveTestGenerated.java index c4377be1d4dc..ee359bd6f2dd 100644 --- a/plugins/kotlin/idea/tests/test/org/jetbrains/jet/resolve/PartialBodyResolveTestGenerated.java +++ b/plugins/kotlin/idea/tests/test/org/jetbrains/jet/resolve/PartialBodyResolveTestGenerated.java @@ -19,6 +19,7 @@ package org.jetbrains.jet.resolve; import com.intellij.testFramework.TestDataPath; import org.jetbrains.jet.JUnit3RunnerWithInners; import org.jetbrains.jet.JetTestUtils; +import org.jetbrains.jet.test.InnerTestClasses; import org.jetbrains.jet.test.TestMetadata; import org.junit.runner.RunWith; @@ -107,6 +108,24 @@ public class PartialBodyResolveTestGenerated extends AbstractPartialBodyResolveT doTest(fileName); } + @TestMetadata("IfNotIsErrorVariable.kt") + public void testIfNotIsErrorVariable() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/IfNotIsErrorVariable.kt"); + doTest(fileName); + } + + @TestMetadata("IfNotIsMyError.kt") + public void testIfNotIsMyError() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/IfNotIsMyError.kt"); + doTest(fileName); + } + + @TestMetadata("IfNotIsNothingProp.kt") + public void testIfNotIsNothingProp() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/IfNotIsNothingProp.kt"); + doTest(fileName); + } + @TestMetadata("IfNotIsReturn.kt") public void testIfNotIsReturn() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/IfNotIsReturn.kt"); diff --git a/plugins/kotlin/idea/tests/testData/resolve/partialBodyResolve/IfNotIsErrorVariable.dump b/plugins/kotlin/idea/tests/testData/resolve/partialBodyResolve/IfNotIsErrorVariable.dump new file mode 100644 index 000000000000..fe169a3d4ae9 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/resolve/partialBodyResolve/IfNotIsErrorVariable.dump @@ -0,0 +1,3 @@ +Resolve target: value-parameter val p: kotlin.Any +Skipped statements: +if (p !is String) { print(error) } diff --git a/plugins/kotlin/idea/tests/testData/resolve/partialBodyResolve/IfNotIsErrorVariable.kt b/plugins/kotlin/idea/tests/testData/resolve/partialBodyResolve/IfNotIsErrorVariable.kt new file mode 100644 index 000000000000..2225f35f217a --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/resolve/partialBodyResolve/IfNotIsErrorVariable.kt @@ -0,0 +1,7 @@ +val error = "error" +fun foo(p: Any) { + if (p !is String) { + print(error) + } + println(p.size) +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/resolve/partialBodyResolve/IfNotIsMyError.dump b/plugins/kotlin/idea/tests/testData/resolve/partialBodyResolve/IfNotIsMyError.dump new file mode 100644 index 000000000000..7578f928cceb --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/resolve/partialBodyResolve/IfNotIsMyError.dump @@ -0,0 +1,2 @@ +Resolve target: value-parameter val p: kotlin.Any smart-casted to kotlin.String +Skipped statements: diff --git a/plugins/kotlin/idea/tests/testData/resolve/partialBodyResolve/IfNotIsMyError.kt b/plugins/kotlin/idea/tests/testData/resolve/partialBodyResolve/IfNotIsMyError.kt new file mode 100644 index 000000000000..035090694222 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/resolve/partialBodyResolve/IfNotIsMyError.kt @@ -0,0 +1,8 @@ +fun myError(): Nothing = throw Exception() + +fun foo(p: Any) { + if (p !is String) { + myError() + } + println(p.size) +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/resolve/partialBodyResolve/IfNotIsNothingProp.dump b/plugins/kotlin/idea/tests/testData/resolve/partialBodyResolve/IfNotIsNothingProp.dump new file mode 100644 index 000000000000..7578f928cceb --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/resolve/partialBodyResolve/IfNotIsNothingProp.dump @@ -0,0 +1,2 @@ +Resolve target: value-parameter val p: kotlin.Any smart-casted to kotlin.String +Skipped statements: diff --git a/plugins/kotlin/idea/tests/testData/resolve/partialBodyResolve/IfNotIsNothingProp.kt b/plugins/kotlin/idea/tests/testData/resolve/partialBodyResolve/IfNotIsNothingProp.kt new file mode 100644 index 000000000000..18ec71594361 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/resolve/partialBodyResolve/IfNotIsNothingProp.kt @@ -0,0 +1,9 @@ +val prop: Nothing + get() = throw Exception() + +fun foo(p: Any) { + if (p !is String) { + prop + } + println(p.size) +} \ No newline at end of file