From 385c15d0e2e81b5e267bc34d46c258530fd0d822 Mon Sep 17 00:00:00 2001 From: "Pavel V. Talanov" Date: Mon, 5 Aug 2013 19:31:43 +0400 Subject: [PATCH] Restructure JetDecompiledData JetDecompiledData now stores file text and uses descriptors serialized to String to find declarations --- .../libraries/DecompiledDataFactory.java | 126 ++++++++---------- .../JetContentBasedFileSubstitutor.java | 11 +- .../plugin/libraries/JetDecompiledData.java | 92 ++++++------- idea/testData/libraries/decompiled/Color.kt | 8 +- .../decompiled/WithInnerAndObject.kt | 4 +- .../decompiled/WithTraitClassObject.kt | 4 +- .../NavigateToDecompiledLibraryTest.java | 49 ++++--- 7 files changed, 134 insertions(+), 160 deletions(-) diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.java b/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.java index c163a03e6d8..35b55154ccc 100644 --- a/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.java +++ b/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.java @@ -16,74 +16,64 @@ package org.jetbrains.jet.plugin.libraries; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiField; -import com.intellij.psi.impl.compiled.ClsElementImpl; -import com.intellij.psi.impl.compiled.ClsFileImpl; -import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.di.InjectorForJavaDescriptorResolver; import org.jetbrains.jet.lang.descriptors.*; -import org.jetbrains.jet.lang.psi.JetDeclaration; import org.jetbrains.jet.lang.psi.JetFile; -import org.jetbrains.jet.lang.resolve.BindingContext; -import org.jetbrains.jet.lang.resolve.BindingContextUtils; import org.jetbrains.jet.lang.resolve.BindingTraceContext; import org.jetbrains.jet.lang.resolve.MemberComparator; -import org.jetbrains.jet.lang.resolve.java.DescriptorResolverUtils; import org.jetbrains.jet.lang.resolve.java.DescriptorSearchRule; import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver; -import org.jetbrains.jet.lang.resolve.java.JvmAbi; +import org.jetbrains.jet.lang.resolve.java.resolver.KotlinClassFileHeader; import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.renderer.DescriptorRenderer; import org.jetbrains.jet.renderer.DescriptorRendererBuilder; import java.util.*; -public class DecompiledDataFactory { +import static org.jetbrains.jet.plugin.libraries.JetDecompiledData.descriptorToKey; + +public final class DecompiledDataFactory { private static final String DECOMPILED_COMMENT = "/* compiled code */"; - private static final DescriptorRenderer DESCRIPTOR_RENDERER = + public static final DescriptorRenderer DESCRIPTOR_RENDERER = new DescriptorRendererBuilder().setWithDefinedIn(false).setClassWithPrimaryConstructor(true).build(); - + @NotNull private final StringBuilder builder = new StringBuilder(); - private final ClsFileImpl clsFile; - private final BindingContext bindingContext; - private final Map clsMembersToRanges = new HashMap(); - - private final Map clsElementsToJetElements = new HashMap(); + @NotNull + private final Map renderedDescriptorsToRange = new HashMap(); + @NotNull private final JavaDescriptorResolver javaDescriptorResolver; + @NotNull + private final KotlinClassFileHeader kotlinClassFileHeader; + @NotNull + private final VirtualFile classFile; + @NotNull + private final Project project; - private DecompiledDataFactory(ClsFileImpl clsFile) { - this.clsFile = clsFile; - - BindingTraceContext trace = new BindingTraceContext(); - this.bindingContext = trace.getBindingContext(); - InjectorForJavaDescriptorResolver injector = new InjectorForJavaDescriptorResolver(clsFile.getProject(), trace); + private DecompiledDataFactory(@NotNull VirtualFile classFile, @NotNull Project project) { + this.classFile = classFile; + this.project = project; + InjectorForJavaDescriptorResolver injector = + new InjectorForJavaDescriptorResolver(project, new BindingTraceContext()); this.javaDescriptorResolver = injector.getJavaDescriptorResolver(); + this.kotlinClassFileHeader = KotlinClassFileHeader.readKotlinHeaderFromClassFile(classFile); } @NotNull - static JetDecompiledData createDecompiledData(@NotNull ClsFileImpl clsFile) { - return new DecompiledDataFactory(clsFile).build(); + static JetDecompiledData createDecompiledData(@NotNull VirtualFile virtualFile, @NotNull Project project) { + return new DecompiledDataFactory(virtualFile, project).build(); } private JetDecompiledData build() { - builder.append("// IntelliJ API Decompiler stub source generated from a class file\n" + - "// Implementation of methods is not available"); - builder.append("\n\n"); - - String packageName = clsFile.getPackageName(); - if (packageName.length() > 0) { - builder.append("package ").append(packageName).append("\n\n"); - } - - PsiClass psiClass = clsFile.getClasses()[0]; - - if (DescriptorResolverUtils.isCompiledKotlinPackageClass(psiClass)) { - NamespaceDescriptor nd = javaDescriptorResolver.resolveNamespace(new FqName(packageName), DescriptorSearchRule.INCLUDE_KOTLIN); - + FqName packageFqName = kotlinClassFileHeader.getJvmClassName().getFqName().parent(); + appendDecompiledTextAndPackageName(packageFqName); + KotlinClassFileHeader.HeaderType type = kotlinClassFileHeader.getType(); + if (type == KotlinClassFileHeader.HeaderType.PACKAGE) { + NamespaceDescriptor nd = javaDescriptorResolver.resolveNamespace(packageFqName, DescriptorSearchRule.INCLUDE_KOTLIN); if (nd != null) { for (DeclarationDescriptor member : sortDeclarations(nd.getMemberScope().getAllDescriptors())) { if (member instanceof ClassDescriptor || member instanceof NamespaceDescriptor @@ -96,29 +86,28 @@ public class DecompiledDataFactory { } } else { - ClassDescriptor cd = javaDescriptorResolver.resolveClass(new FqName(psiClass.getQualifiedName()), DescriptorSearchRule.INCLUDE_KOTLIN); + assert type == KotlinClassFileHeader.HeaderType.CLASS; + ClassDescriptor cd = javaDescriptorResolver.resolveClass(kotlinClassFileHeader.getJvmClassName().getFqName(), + DescriptorSearchRule.INCLUDE_KOTLIN); if (cd != null) { appendDescriptor(cd, ""); } } - JetFile jetFile = JetDummyClassFileViewProvider.createJetFile(clsFile.getManager(), clsFile.getVirtualFile(), builder.toString()); - for (Map.Entry clsMemberToRange : clsMembersToRanges.entrySet()) { - PsiElement clsMember = clsMemberToRange.getKey(); - assert clsMember instanceof ClsElementImpl; - - TextRange range = clsMemberToRange.getValue(); - JetDeclaration jetDeclaration = PsiTreeUtil.findElementOfClassAtRange(jetFile, range.getStartOffset(), range.getEndOffset(), - JetDeclaration.class); - assert jetDeclaration != null : "Can't find declaration at " + range + ": " - + jetFile.getText().substring(range.getStartOffset(), range.getEndOffset()); - clsElementsToJetElements.put((ClsElementImpl) clsMember, jetDeclaration); - } - - return new JetDecompiledData(jetFile, clsElementsToJetElements); + JetFile jetFile = JetDummyClassFileViewProvider.createJetFile(PsiManager.getInstance(project), classFile, builder.toString()); + return new JetDecompiledData(jetFile, renderedDescriptorsToRange); } - private static List sortDeclarations(Collection input) { + private void appendDecompiledTextAndPackageName(@NotNull FqName packageName) { + builder.append("// IntelliJ API Decompiler stub source generated from a class file\n" + + "// Implementation of methods is not available"); + builder.append("\n\n"); + if (!packageName.isRoot()) { + builder.append("package ").append(packageName).append("\n\n"); + } + } + + private static List sortDeclarations(@NotNull Collection input) { ArrayList r = new ArrayList(input); Collections.sort(r, MemberComparator.INSTANCE); return r; @@ -150,10 +139,11 @@ public class DecompiledDataFactory { ClassDescriptor classDescriptor = (ClassDescriptor) descriptor; boolean firstPassed = false; String subindent = indent + " "; - if (classDescriptor.getClassObjectDescriptor() != null) { + ClassDescriptor classObjectDescriptor = classDescriptor.getClassObjectDescriptor(); + if (classObjectDescriptor != null) { firstPassed = true; builder.append(subindent); - appendDescriptor(classDescriptor.getClassObjectDescriptor(), subindent); + appendDescriptor(classObjectDescriptor, subindent); } for (DeclarationDescriptor member : sortDeclarations(classDescriptor.getDefaultType().getMemberScope().getAllDescriptors())) { if (member.getContainingDeclaration() != descriptor) { @@ -180,28 +170,18 @@ public class DecompiledDataFactory { } builder.append("\n"); - saveClsMemberToRange(descriptor, startOffset, endOffset); + saveDescriptorToRange(descriptor, startOffset, endOffset); if (descriptor instanceof ClassDescriptor) { ConstructorDescriptor primaryConstructor = ((ClassDescriptor) descriptor).getUnsubstitutedPrimaryConstructor(); if (primaryConstructor != null) { - saveClsMemberToRange(primaryConstructor, startOffset, endOffset); + saveDescriptorToRange(primaryConstructor, startOffset, endOffset); } } } - private void saveClsMemberToRange(DeclarationDescriptor descriptor, int startOffset, int endOffset) { - PsiElement clsMember = BindingContextUtils.descriptorToDeclaration(bindingContext, descriptor); - if (clsMember != null) { - clsMembersToRanges.put(clsMember, new TextRange(startOffset, endOffset)); - - if (descriptor instanceof ClassDescriptor && ((ClassDescriptor) descriptor).getKind() == ClassKind.OBJECT) { - assert clsMember instanceof PsiClass; - PsiField instanceField = ((PsiClass) clsMember).findFieldByName(JvmAbi.INSTANCE_FIELD, false); - assert instanceField != null; - clsMembersToRanges.put(instanceField, new TextRange(startOffset, endOffset)); - } - } + private void saveDescriptorToRange(DeclarationDescriptor descriptor, int startOffset, int endOffset) { + renderedDescriptorsToRange.put(descriptorToKey(descriptor), new TextRange(startOffset, endOffset)); } private static boolean isNamedObjectProperty(@NotNull DeclarationDescriptor descriptor) { diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/JetContentBasedFileSubstitutor.java b/idea/src/org/jetbrains/jet/plugin/libraries/JetContentBasedFileSubstitutor.java index b630d24da8f..af1d2cc604f 100644 --- a/idea/src/org/jetbrains/jet/plugin/libraries/JetContentBasedFileSubstitutor.java +++ b/idea/src/org/jetbrains/jet/plugin/libraries/JetContentBasedFileSubstitutor.java @@ -23,12 +23,11 @@ import com.intellij.openapi.fileTypes.SyntaxHighlighter; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.impl.compiled.ClsFileImpl; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.plugin.highlighter.JetHighlighter; -public class JetContentBasedFileSubstitutor implements ContentBasedClassFileProcessor { +public final class JetContentBasedFileSubstitutor implements ContentBasedClassFileProcessor { @Override public boolean isApplicable(@Nullable Project project, @NotNull final VirtualFile file) { @@ -48,16 +47,14 @@ public class JetContentBasedFileSubstitutor implements ContentBasedClassFileProc return false; } - return JetDecompiledData.isKotlinFile(project, file); + return DecompiledUtils.isKotlinCompiledFile(file); } @NotNull @Override public String obtainFileText(Project project, VirtualFile file) { - if (JetDecompiledData.isKotlinFile(project, file)) { - ClsFileImpl clsFile = JetDecompiledData.getClsFile(project, file); - assert clsFile != null; - return JetDecompiledData.getDecompiledData(clsFile).getJetFile().getText(); + if (DecompiledUtils.isKotlinCompiledFile(file)) { + return JetDecompiledData.getDecompiledData(file, project).getFileText(); } return ""; } diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/JetDecompiledData.java b/idea/src/org/jetbrains/jet/plugin/libraries/JetDecompiledData.java index 106f95665d6..459fb6f479f 100644 --- a/idea/src/org/jetbrains/jet/plugin/libraries/JetDecompiledData.java +++ b/idea/src/org/jetbrains/jet/plugin/libraries/JetDecompiledData.java @@ -16,85 +16,77 @@ package org.jetbrains.jet.plugin.libraries; -import com.intellij.ide.highlighter.JavaClassFileType; -import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiManager; -import com.intellij.psi.impl.compiled.ClsElementImpl; -import com.intellij.psi.impl.compiled.ClsFileImpl; +import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.psi.JetDeclaration; import org.jetbrains.jet.lang.psi.JetFile; -import org.jetbrains.jet.lang.resolve.java.DescriptorResolverUtils; import java.util.Map; @SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized") public class JetDecompiledData { - private final JetFile jetFile; - private final Map clsElementsToJetElements; - private static final Object LOCK = new String("decompiled data lock"); private static final Key USER_DATA_KEY = new Key("USER_DATA_KEY"); + private static final Object LOCK = new String("decompiled data lock"); - JetDecompiledData(JetFile jetFile, Map clsElementJetDeclarationMap) { - this.jetFile = jetFile; - clsElementsToJetElements = clsElementJetDeclarationMap; + @NotNull + private final JetFile file; + @NotNull + private final Map renderedDescriptorsToRanges; + + JetDecompiledData(@NotNull JetFile file, @NotNull Map renderedDescriptorsToRanges) { + this.file = file; + this.renderedDescriptorsToRanges = renderedDescriptorsToRanges; } @NotNull - public JetFile getJetFile() { - return jetFile; - } - - public JetDeclaration getJetDeclarationByClsElement(ClsElementImpl clsElement) { - return clsElementsToJetElements.get(clsElement); - } - - @Nullable - public static ClsFileImpl getClsFile(@NotNull Project project, @NotNull VirtualFile vFile) { - if (!FileTypeManager.getInstance().isFileOfType(vFile, JavaClassFileType.INSTANCE)) { - return null; - } - PsiFile psiFile = PsiManager.getInstance(project).findFile(vFile); - if (!(psiFile instanceof ClsFileImpl)) { - return null; - } - ClsFileImpl clsFile = (ClsFileImpl) psiFile; - if (clsFile.getClasses().length != 1) { - return null; - } - return clsFile; - } - - public static boolean isKotlinFile(@NotNull Project project, @NotNull VirtualFile vFile) { - ClsFileImpl clsFile = getClsFile(project, vFile); - return clsFile != null && isKotlinFile(clsFile); - } - - public static boolean isKotlinFile(@NotNull ClsFileImpl clsFile) { - return DescriptorResolverUtils.isCompiledKotlinClassOrPackageClass(clsFile.getClasses()[0]); + public String getFileText() { + return file.getText(); } @NotNull - public static JetDecompiledData getDecompiledData(@NotNull ClsFileImpl clsFile) { + public static JetDecompiledData getDecompiledData(@NotNull VirtualFile virtualFile, @NotNull Project project) { synchronized (LOCK) { - if (clsFile.getUserData(USER_DATA_KEY) == null) { - clsFile.putUserData(USER_DATA_KEY, DecompiledDataFactory.createDecompiledData(clsFile)); + if (virtualFile.getUserData(USER_DATA_KEY) == null) { + virtualFile.putUserData(USER_DATA_KEY, DecompiledDataFactory.createDecompiledData(virtualFile, project)); } - JetDecompiledData decompiledData = clsFile.getUserData(USER_DATA_KEY); + JetDecompiledData decompiledData = virtualFile.getUserData(USER_DATA_KEY); assert decompiledData != null; return decompiledData; } } @TestOnly - Map getClsElementsToJetElements() { - return clsElementsToJetElements; + @NotNull + public Map getRenderedDescriptorsToRanges() { + return renderedDescriptorsToRanges; + } + + @Nullable + public JetDeclaration getDeclarationForDescriptor(@NotNull DeclarationDescriptor descriptor) { + String key = descriptorToKey(descriptor); + TextRange range = renderedDescriptorsToRanges.get(key); + if (range == null) { + return null; + } + return PsiTreeUtil.findElementOfClassAtRange(file, range.getStartOffset(), range.getEndOffset(), JetDeclaration.class); + } + + //TODO: should use more accurate way to identify descriptors + @NotNull + static String descriptorToKey(@NotNull DeclarationDescriptor descriptor) { + return DecompiledDataFactory.DESCRIPTOR_RENDERER.render(descriptor); + } + + @NotNull + public JetFile getFile() { + return file; } } diff --git a/idea/testData/libraries/decompiled/Color.kt b/idea/testData/libraries/decompiled/Color.kt index fd07142a18f..180f27249f1 100644 --- a/idea/testData/libraries/decompiled/Color.kt +++ b/idea/testData/libraries/decompiled/Color.kt @@ -4,17 +4,17 @@ package testData.libraries [[public final enum class Color(rgb: jet.Int) : jet.Enum { - public class object { + [[public class object { [public final val BLUE: testData.libraries.Color] /* compiled code */ [public final val GREEN: testData.libraries.Color] /* compiled code */ [public final val RED: testData.libraries.Color] /* compiled code */ - public final fun valueOf(value: jet.String): testData.libraries.Color { /* compiled code */ } + [public final fun valueOf(value: jet.String): testData.libraries.Color { /* compiled code */ }] - public final fun values(): jet.Array { /* compiled code */ } - } + [public final fun values(): jet.Array { /* compiled code */ }] + }]] [internal final val rgb: jet.Int] /* compiled code */ }]] \ No newline at end of file diff --git a/idea/testData/libraries/decompiled/WithInnerAndObject.kt b/idea/testData/libraries/decompiled/WithInnerAndObject.kt index 791935a9178..cb681709dfa 100644 --- a/idea/testData/libraries/decompiled/WithInnerAndObject.kt +++ b/idea/testData/libraries/decompiled/WithInnerAndObject.kt @@ -4,9 +4,9 @@ package testData.libraries [[public final class WithInnerAndObject() { - public class object { + [[public class object { [internal final fun foo(): jet.Unit { /* compiled code */ }] - } + }]] [[internal final class MyInner() { [internal trait MyInnerInner { diff --git a/idea/testData/libraries/decompiled/WithTraitClassObject.kt b/idea/testData/libraries/decompiled/WithTraitClassObject.kt index 114e9cef747..9fbad9f6611 100644 --- a/idea/testData/libraries/decompiled/WithTraitClassObject.kt +++ b/idea/testData/libraries/decompiled/WithTraitClassObject.kt @@ -4,6 +4,6 @@ package testData.libraries [[public final class WithTraitClassObject() { - public class object : testData.libraries.SimpleTrait { - } + [[public class object : testData.libraries.SimpleTrait { + }]] }]] diff --git a/idea/tests/org/jetbrains/jet/plugin/libraries/NavigateToDecompiledLibraryTest.java b/idea/tests/org/jetbrains/jet/plugin/libraries/NavigateToDecompiledLibraryTest.java index 6eb1027478c..7ae478fe1b0 100644 --- a/idea/tests/org/jetbrains/jet/plugin/libraries/NavigateToDecompiledLibraryTest.java +++ b/idea/tests/org/jetbrains/jet/plugin/libraries/NavigateToDecompiledLibraryTest.java @@ -16,18 +16,21 @@ package org.jetbrains.jet.plugin.libraries; +import com.beust.jcommander.internal.Maps; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiFile; -import com.intellij.psi.impl.compiled.ClsElementImpl; -import com.intellij.psi.impl.compiled.ClsFileImpl; +import com.intellij.psi.util.PsiTreeUtil; +import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.psi.JetDeclaration; +import org.jetbrains.jet.lang.psi.JetFile; import java.util.Map; +import static org.jetbrains.jet.plugin.libraries.JetDecompiledData.getDecompiledData; + public class NavigateToDecompiledLibraryTest extends AbstractNavigateToLibraryTest { private VirtualFile classFile; @@ -77,15 +80,15 @@ public class NavigateToDecompiledLibraryTest extends AbstractNavigateToLibraryTe private void doTest() { classFile = getClassFile(); - - Map map = getDecompiledData(classFile).getClsElementsToJetElements(); - checkNavigationElements(map); + JetDecompiledData decompiledData = JetDecompiledData.getDecompiledData(classFile, getProject()); + Map map = getRenderedDescriptorToKotlinPsiMap(decompiledData.getFile(), + getDecompiledData(classFile, getProject()) .getRenderedDescriptorsToRanges()); String decompiledTextWithMarks = getDecompiledTextWithMarks(map); assertSameLinesWithFile(TEST_DATA_PATH + "/decompiled/" + getTestName(false) + ".kt", decompiledTextWithMarks); } - private String getDecompiledTextWithMarks(Map map) { + private String getDecompiledTextWithMarks(Map map) { String decompiledText = getDecompiledText(); int[] openings = new int[decompiledText.length() + 1]; @@ -113,21 +116,6 @@ public class NavigateToDecompiledLibraryTest extends AbstractNavigateToLibraryTe return document.getText(); } - private JetDecompiledData getDecompiledData(VirtualFile classFile) { - PsiFile classPsiFile = getPsiManager().findFile(classFile); - assertInstanceOf(classPsiFile, ClsFileImpl.class); - ClsFileImpl clsFile = (ClsFileImpl) classPsiFile; - return JetDecompiledData.getDecompiledData(clsFile); - } - - private void checkNavigationElements(Map map) { - PsiFile classPsiFile = getPsiManager().findFile(classFile); - for (Map.Entry clsToJet : map.entrySet()) { - assertSame(classPsiFile, clsToJet.getKey().getContainingFile()); - assertSame(clsToJet.getValue(), clsToJet.getKey().getNavigationElement()); - } - } - private VirtualFile getClassFile() { VirtualFile packageDir = libraryDir.findFileByRelativePath(PACKAGE.replace(".", "/")); assertNotNull(packageDir); @@ -140,4 +128,21 @@ public class NavigateToDecompiledLibraryTest extends AbstractNavigateToLibraryTe protected boolean isWithSources() { return false; } + + @NotNull + private static Map getRenderedDescriptorToKotlinPsiMap( + @NotNull JetFile file, @NotNull Map renderedDescriptorsToRanges + ) { + Map renderedDescriptorsToJetDeclarations = Maps.newHashMap(); + for (Map.Entry renderedDescriptorToRange : renderedDescriptorsToRanges.entrySet()) { + String renderedDescriptor = renderedDescriptorToRange.getKey(); + TextRange range = renderedDescriptorToRange.getValue(); + JetDeclaration jetDeclaration = PsiTreeUtil.findElementOfClassAtRange(file, range.getStartOffset(), range.getEndOffset(), + JetDeclaration.class); + assert jetDeclaration != null : "Can't find declaration at " + range + ": " + + file.getText().substring(range.getStartOffset(), range.getEndOffset()); + renderedDescriptorsToJetDeclarations.put(renderedDescriptor, jetDeclaration); + } + return renderedDescriptorsToJetDeclarations; + } } \ No newline at end of file