From bc816851f1fc3ed9f20e1d7c46d5de95aa174b9f Mon Sep 17 00:00:00 2001 From: "Pavel V. Talanov" Date: Tue, 1 Dec 2015 13:11:51 +0300 Subject: [PATCH] getModuleInfo: Provide utility to default to null instead of logging an error Use it to workaround cases when java resolve references some unexpected classes/files, referencing non-physical Dummy.java in particular --- .../kotlin/resolve/jvm/JvmAnalyzerFacade.kt | 7 ++++-- .../caches/resolve/ModuleDependencyMapper.kt | 2 +- .../idea/caches/resolve/getModuleInfo.kt | 24 +++++++++++-------- .../caches/resolve/CustomModuleInfoTest.kt | 7 ++++++ 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/JvmAnalyzerFacade.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/JvmAnalyzerFacade.kt index f113ea15342..7ef951f718f 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/JvmAnalyzerFacade.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/JvmAnalyzerFacade.kt @@ -40,7 +40,7 @@ import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory import java.util.* public class JvmPlatformParameters( - public val moduleByJavaClass: (JavaClass) -> ModuleInfo + public val moduleByJavaClass: (JavaClass) -> ModuleInfo? ) : PlatformAnalysisParameters @@ -68,7 +68,10 @@ public object JvmAnalyzerFacade : AnalyzerFacade() { // We don't have full control over idea resolve api so we allow for a situation which should not happen in Kotlin. // For example, type in a java library can reference a class declared in a source root (is valid but rare case) // Providing a fallback strategy in this case can hide future problems, so we should at least log to be able to diagnose those - val resolverForReferencedModule = resolverForProject.tryGetResolverForModule(referencedClassModule as M) + + @Suppress("UNCHECKED_CAST") + val resolverForReferencedModule = referencedClassModule?.let { resolverForProject.tryGetResolverForModule(it as M) } + val resolverForModule = resolverForReferencedModule ?: run { LOG.warn("Java referenced $referencedClassModule from $moduleInfo\nReferenced class was: $javaClass\n") // in case referenced class lies outside of our resolver, resolve the class as if it is inside our module diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ModuleDependencyMapper.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ModuleDependencyMapper.kt index 5c21e4f15c5..d4ce2ce6742 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ModuleDependencyMapper.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ModuleDependencyMapper.kt @@ -59,7 +59,7 @@ fun createModuleResolverProvider( val jvmPlatformParameters = JvmPlatformParameters { javaClass: JavaClass -> val psiClass = (javaClass as JavaClassImpl).getPsi() - psiClass.getModuleInfo() + psiClass.getNullableModuleInfo() } val resolverForProject = analyzerFacade.setupResolverForProject( diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/getModuleInfo.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/getModuleInfo.kt index 2973b84d9d0..cc3560e1910 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/getModuleInfo.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/getModuleInfo.kt @@ -31,12 +31,17 @@ import org.jetbrains.kotlin.idea.util.ProjectRootsUtil import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.utils.sure -fun PsiElement.getModuleInfo(): IdeaModuleInfo { - fun logAndReturnDefault(message: String): IdeaModuleInfo { - LOG.error("Could not find correct module information.\nReason: $message") - return NotUnderContentRootModuleInfo - } +fun PsiElement.getModuleInfo(): IdeaModuleInfo = this.getModuleInfo { reason -> + LOG.error("Could not find correct module information.\nReason: $reason") + NotUnderContentRootModuleInfo +}.sure { "Defaulting to NotUnderContentRootModuleInfo so null is not possible" } +fun PsiElement.getNullableModuleInfo(): IdeaModuleInfo? = this.getModuleInfo { reason -> + LOG.warn("Could not find correct module information.\nReason: $reason") + null +} + +private fun PsiElement.getModuleInfo(onFailure: (String) -> IdeaModuleInfo?): IdeaModuleInfo? { if (this is KtLightElement<*, *>) return this.getModuleInfoForLightElement() val containingJetFile = (this as? KtElement)?.getContainingFile() as? KtFile @@ -45,7 +50,7 @@ fun PsiElement.getModuleInfo(): IdeaModuleInfo { val doNotAnalyze = containingJetFile?.doNotAnalyze if (doNotAnalyze != null) { - return logAndReturnDefault( + return onFailure( "Should not analyze element: ${getText()} in file ${containingJetFile?.getName() ?: " "}\n$doNotAnalyze" ) } @@ -55,15 +60,14 @@ fun PsiElement.getModuleInfo(): IdeaModuleInfo { if (containingJetFile is KtCodeFragment) { return containingJetFile.getContext()?.getModuleInfo() - ?: logAndReturnDefault("Analyzing code fragment of type ${containingJetFile.javaClass} with no context element\nText:\n${containingJetFile.getText()}") + ?: onFailure("Analyzing code fragment of type ${containingJetFile.javaClass} with no context element\nText:\n${containingJetFile.getText()}") } - val project = getProject() val containingFile = getContainingFile() - ?: return logAndReturnDefault("Analyzing element of type $javaClass with no containing file\nText:\n${getText()}") + ?: return onFailure("Analyzing element of type $javaClass with no containing file\nText:\n${getText()}") val virtualFile = containingFile.getOriginalFile().getVirtualFile() - ?: return logAndReturnDefault("Analyzing non-physical file $containingFile of type ${containingFile.javaClass}") + ?: return onFailure("Analyzing non-physical file $containingFile of type ${containingFile.javaClass}") return getModuleInfoByVirtualFile( project, diff --git a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/CustomModuleInfoTest.kt b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/CustomModuleInfoTest.kt index 07c210b5a11..cb022a037b1 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/CustomModuleInfoTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/CustomModuleInfoTest.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.idea.caches.resolve import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiElementFactory import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor @@ -36,4 +37,10 @@ public class CustomModuleInfoTest : KotlinLightCodeInsightFixtureTestCase() { Assert.assertEquals("Members of decompiled class should have the same module info", classModuleInfo, it.getModuleInfo()) } } + + fun testModuleInfoForPsiCreatedByJavaPsiFactory() { + val dummyClass = PsiElementFactory.SERVICE.getInstance(project).createClass("A") + val moduleInfo = dummyClass.getNullableModuleInfo() + Assert.assertEquals("Should be null for psi created by factory", null, moduleInfo) + } }