diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsJavaCodeReferenceElementImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsJavaCodeReferenceElementImpl.java index 4d713b0e4f52..89eddafc52f9 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsJavaCodeReferenceElementImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsJavaCodeReferenceElementImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -17,7 +17,6 @@ package com.intellij.psi.impl.compiled; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.impl.PsiSubstitutorImpl; import com.intellij.psi.impl.ResolveScopeManager; @@ -33,6 +32,7 @@ import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.HashMap; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -143,7 +143,7 @@ public class ClsJavaCodeReferenceElementImpl extends ClsElementImpl implements P final Map substitutionMap) { final PsiClass containingClass = psiClass.getContainingClass(); if (containingClass != null && !containingClass.hasModifierProperty(PsiModifier.STATIC)) { - final String outerClassRef = StringUtil.getPackageName(canonicalText); + final String outerClassRef = getOuterClassRef(canonicalText); final String[] classParameters = PsiNameHelper.getClassParametersText(outerClassRef); final PsiType[] args = classParameters.length == 0 ? null : new ClsReferenceParameterListImpl(this, classParameters).getTypeArguments(); final PsiTypeParameter[] typeParameters = containingClass.getTypeParameters(); @@ -156,6 +156,27 @@ public class ClsJavaCodeReferenceElementImpl extends ClsElementImpl implements P } } + @NotNull + @Contract(pure = true) + private static String getOuterClassRef(String ref) { + int stack = 0; + for (int i = ref.length() - 1; i >= 0; i--) { + char c = ref.charAt(i); + switch (c) { + case '<': + stack--; + break; + case '>': + stack++; + break; + case '.': + if (stack == 0) return ref.substring(0, i); + } + } + + return ""; + } + @Override @NotNull public JavaResolveResult advancedResolve(boolean incompleteCode) { diff --git a/java/java-tests/testData/libResolve/genericSignature/ParameterizedTypes$Inner.class b/java/java-tests/testData/libResolve/genericSignature/ParameterizedTypes$Inner.class new file mode 100644 index 000000000000..0833dec8203b Binary files /dev/null and b/java/java-tests/testData/libResolve/genericSignature/ParameterizedTypes$Inner.class differ diff --git a/java/java-tests/testData/libResolve/genericSignature/ParameterizedTypes.class b/java/java-tests/testData/libResolve/genericSignature/ParameterizedTypes.class new file mode 100644 index 000000000000..10117a15dbe6 Binary files /dev/null and b/java/java-tests/testData/libResolve/genericSignature/ParameterizedTypes.class differ diff --git a/java/java-tests/testSrc/com/intellij/psi/resolve/ResolveInLibrariesTest.groovy b/java/java-tests/testSrc/com/intellij/psi/resolve/ResolveInLibrariesTest.groovy index ce5c6575fb86..b2394a8e763e 100644 --- a/java/java-tests/testSrc/com/intellij/psi/resolve/ResolveInLibrariesTest.groovy +++ b/java/java-tests/testSrc/com/intellij/psi/resolve/ResolveInLibrariesTest.groovy @@ -22,7 +22,9 @@ import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.JavaPsiFacade import com.intellij.psi.PsiClass +import com.intellij.psi.PsiClassType import com.intellij.psi.PsiMethod +import com.intellij.psi.impl.JavaPsiFacadeEx import com.intellij.psi.impl.source.PsiFileImpl import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.searches.ClassInheritorsSearch @@ -236,4 +238,51 @@ class TestCase { assert method.hierarchicalMethodSignature.superSignatures.size() == 0 } + public void "test nested generic signature from binary"() { + myFixture.testDataPath = PathManagerEx.getTestDataPath() + "/libResolve/genericSignature" + myFixture.copyDirectoryToProject("", "lib") + PsiTestUtil.addLibrary(myModule, "lib", myFixture.tempDirFixture.getFile("").path, "lib"); + + def javaPsiFacade = JavaPsiFacadeEx.getInstanceEx(project) + def factory = javaPsiFacade.elementFactory + + def parameterizedTypes = JavaPsiFacade.getInstance(project).findClass('pkg.ParameterizedTypes', GlobalSearchScope.allScope(project)) + assert parameterizedTypes + + def parameterP = parameterizedTypes.typeParameters[0] + assert parameterP.name == 'P' + + def classInner = parameterizedTypes.innerClasses[0] + assert classInner.name == 'Inner' + + def parameterI = classInner.typeParameters[0] + assert parameterI.name == 'I' + + def unspecificMethod = parameterizedTypes.findMethodsByName("getUnspecificInner", false)[0] + def unspecificReturnType = unspecificMethod.returnType as PsiClassType + assert unspecificReturnType.canonicalText == 'pkg.ParameterizedTypes

.Inner' + + def unspecificResolveResult = unspecificReturnType.resolveGenerics() + assert unspecificResolveResult.element == classInner + + def unspecificOuter = factory.createType(parameterizedTypes, unspecificResolveResult.substitutor); + assert unspecificOuter.canonicalText == 'pkg.ParameterizedTypes

' + + def specificMethod = parameterizedTypes.findMethodsByName("getSpecificInner", false)[0] + def specificReturnType = specificMethod.returnType as PsiClassType + assert specificReturnType.canonicalText == 'pkg.ParameterizedTypes.Inner' + + def specificResolveResult = specificReturnType.resolveGenerics() + assert specificResolveResult.element == classInner + + def substitutor = specificResolveResult.substitutor + def substitutionMap = substitutor.substitutionMap + assert substitutionMap.containsKey(parameterI) + assert substitutionMap.containsKey(parameterP) + assert substitutor.substitute(parameterP).canonicalText == 'java.lang.Number' + + def specificOuter = factory.createType(parameterizedTypes, specificResolveResult.substitutor); + assert specificOuter.canonicalText == 'pkg.ParameterizedTypes' + } + }