diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/JetSourceNavigationHelper.java b/idea/src/org/jetbrains/jet/plugin/libraries/JetSourceNavigationHelper.java index a33ed158daa..1d0498bf1a9 100644 --- a/idea/src/org/jetbrains/jet/plugin/libraries/JetSourceNavigationHelper.java +++ b/idea/src/org/jetbrains/jet/plugin/libraries/JetSourceNavigationHelper.java @@ -50,6 +50,7 @@ import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; import org.jetbrains.jet.lexer.JetTokens; +import org.jetbrains.jet.plugin.stubindex.JetFullClassNameIndex; import org.jetbrains.jet.plugin.stubindex.JetTopLevelFunctionsFqnNameIndex; import org.jetbrains.jet.plugin.stubindex.JetTopLevelPropertiesFqnNameIndex; import org.jetbrains.jet.renderer.DescriptorRenderer; @@ -100,15 +101,26 @@ public class JetSourceNavigationHelper { @Nullable public static JetClassOrObject getSourceClassOrObject(@NotNull JetClassOrObject decompiledClassOrObject) { - Pair bindingContextAndClassDescriptor = getBindingContextAndClassDescriptor( - decompiledClassOrObject); - if (bindingContextAndClassDescriptor == null) return null; - PsiElement declaration = BindingContextUtils.classDescriptorToDeclaration( - bindingContextAndClassDescriptor.first, bindingContextAndClassDescriptor.second); - if (declaration == null) { - throw new IllegalStateException("class not found by " + bindingContextAndClassDescriptor.second); + if (decompiledClassOrObject instanceof JetObjectDeclaration && decompiledClassOrObject.getParent() instanceof JetClassObject) { + // class object case + + JetClass decompiledClass = PsiTreeUtil.getParentOfType(decompiledClassOrObject, JetClass.class); + assert decompiledClass != null; + + JetClass sourceClass = (JetClass) getSourceForNamedClassOrObject(decompiledClass); + if (sourceClass == null) { + return null; + } + + if (sourceClass.hasModifier(JetTokens.ENUM_KEYWORD)) { + return sourceClass; + } + + JetClassObject classObject = sourceClass.getClassObject(); + assert classObject != null; + return classObject.getObjectDeclaration(); } - return (JetClassOrObject) declaration; + return getSourceForNamedClassOrObject(decompiledClassOrObject); } @NotNull @@ -267,6 +279,27 @@ public class JetSourceNavigationHelper { return candidates.isEmpty() ? null : candidates.iterator().next(); } + @Nullable + private static JetNamedDeclaration findSpecialProperty(@NotNull Name memberName, @NotNull JetClass containingClass) { + // property constructor parameters + List constructorParameters = containingClass.getPrimaryConstructorParameters(); + for (JetParameter constructorParameter : constructorParameters) { + if (memberName.equals(constructorParameter.getNameAsName()) && constructorParameter.getValOrVarNode() != null) { + return constructorParameter; + } + } + + // enum entries + if (containingClass.hasModifier(JetTokens.ENUM_KEYWORD)) { + for (JetEnumEntry enumEntry : ContainerUtil.findAll(containingClass.getDeclarations(), JetEnumEntry.class)) { + if (memberName.equals(enumEntry.getNameAsName())) { + return enumEntry; + } + } + } + return null; + } + @Nullable private static JetNamedDeclaration getSourcePropertyOrFunction( @@ -277,22 +310,43 @@ public class JetSourceNavigationHelper { assert memberNameAsString != null; Name memberName = Name.identifier(memberNameAsString); - if (decompiledDeclaration.getParent() instanceof JetFile) { - Collection candidates = getInitialCandidates(decompiledDeclaration, navigationStrategy); + PsiElement decompiledContainer = decompiledDeclaration.getParent(); - candidates = filterByReceiverPresenceAndParametersCount(decompiledDeclaration, navigationStrategy, candidates); + Collection candidates; + if (decompiledContainer instanceof JetFile) { + candidates = getInitialCandidates(decompiledDeclaration, navigationStrategy); + } + else if (decompiledContainer instanceof JetClassBody) { + JetClassOrObject decompiledClassOrObject = (JetClassOrObject) decompiledContainer.getParent(); + JetClassOrObject sourceClassOrObject = getSourceClassOrObject(decompiledClassOrObject); + + candidates = sourceClassOrObject == null + ? Collections.emptyList() + : getInitialCandidates(sourceClassOrObject, memberName, navigationStrategy.getDeclarationClass()); + + if (candidates.isEmpty()) { + if (decompiledDeclaration instanceof JetProperty && sourceClassOrObject instanceof JetClass) { + return findSpecialProperty(memberName, (JetClass) sourceClassOrObject); + } + } + } + else { + throw new IllegalStateException("Unexpected container of decompiled declaration: " + + decompiledContainer.getClass().getSimpleName()); + } + + candidates = filterByReceiverPresenceAndParametersCount(decompiledDeclaration, navigationStrategy, candidates); + + if (candidates.size() <= 1) { + return getUnambiguousCandidate(candidates); + } + + if (!haveRenamesInImports(getContainingFiles(candidates))) { + candidates = filterByReceiverAndParameterTypes(decompiledDeclaration, navigationStrategy, candidates); if (candidates.size() <= 1) { return getUnambiguousCandidate(candidates); } - - if (!haveRenamesInImports(getContainingFiles(candidates))) { - candidates = filterByReceiverAndParameterTypes(decompiledDeclaration, navigationStrategy, candidates); - - if (candidates.size() <= 1) { - return getUnambiguousCandidate(candidates); - } - } } Pair contextAndMemberScope = getBindingContextAndMemberScopeForLibrarySources(decompiledDeclaration); @@ -313,6 +367,23 @@ public class JetSourceNavigationHelper { return null; } + @Nullable + private static JetClassOrObject getSourceForNamedClassOrObject(@NotNull JetClassOrObject decompiledClassOrObject) { + FqName classFqName = JetPsiUtil.getFQName((JetNamedDeclaration) decompiledClassOrObject); + assert classFqName != null; + + GlobalSearchScope librarySourcesScope = createLibrarySourcesScope((JetNamedDeclaration) decompiledClassOrObject); + if (librarySourcesScope == GlobalSearchScope.EMPTY_SCOPE) { // .getProject() == null for EMPTY_SCOPE, and this breaks code + return null; + } + Collection classes = JetFullClassNameIndex.getInstance() + .get(classFqName.getFqName(), decompiledClassOrObject.getProject(), librarySourcesScope); + if (classes.isEmpty()) { + return null; + } + return classes.iterator().next(); // if there are more than one class with this FQ, find first of them + } + @NotNull private static Collection getInitialCandidates( @NotNull Decl decompiledDeclaration, @@ -329,6 +400,21 @@ public class JetSourceNavigationHelper { return index.get(memberFqName.getFqName(), decompiledDeclaration.getProject(), librarySourcesScope); } + @NotNull + private static List getInitialCandidates( + @NotNull JetClassOrObject sourceClassOrObject, + @NotNull final Name name, + @NotNull Class declarationClass + ) { + List allByClass = ContainerUtil.findAll(sourceClassOrObject.getDeclarations(), declarationClass); + return ContainerUtil.filter(allByClass, new Condition() { + @Override + public boolean value(Decl declaration) { + return name.equals(declaration.getNameAsSafeName()); + } + }); + } + @NotNull private static List filterByReceiverPresenceAndParametersCount( final @NotNull Decl decompiledDeclaration, @@ -396,6 +482,9 @@ public class JetSourceNavigationHelper { } private interface NavigationStrategy { + @NotNull + Class getDeclarationClass(); + boolean declarationsMatchByParameterCount(@NotNull Decl a, @NotNull Decl b); boolean declarationsMatchByParameterTypes(@NotNull Decl a, @NotNull Decl b); @@ -411,6 +500,12 @@ public class JetSourceNavigationHelper { } private static class FunctionNavigationStrategy implements NavigationStrategy { + @NotNull + @Override + public Class getDeclarationClass() { + return JetNamedFunction.class; + } + @Override public boolean declarationsMatchByParameterCount(@NotNull JetNamedFunction a, @NotNull JetNamedFunction b) { return a.getValueParameters().size() == b.getValueParameters().size(); @@ -488,6 +583,12 @@ public class JetSourceNavigationHelper { } private static class PropertyNavigationStrategy implements NavigationStrategy { + @NotNull + @Override + public Class getDeclarationClass() { + return JetProperty.class; + } + @Override public boolean declarationsMatchByParameterCount(@NotNull JetProperty a, @NotNull JetProperty b) { return true;