KT-2145 Impossible to depend on Scala library

This commit is contained in:
Maxim Shafirov
2012-06-05 18:15:07 +04:00
parent ed1348ccb3
commit 9b5731595e
5 changed files with 61 additions and 187 deletions
@@ -16,10 +16,11 @@
package com.intellij.psi.impl.compiled;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.impl.source.JavaFileElementType;
import com.intellij.psi.stubs.BinaryFileStubBuilder;
import com.intellij.psi.stubs.PsiFileStub;
import com.intellij.psi.stubs.StubElement;
import com.intellij.util.cls.ClsFormatException;
@@ -29,11 +30,7 @@ import com.intellij.util.cls.ClsFormatException;
public class ClassFileStubBuilder implements BinaryFileStubBuilder {
@Override
public boolean acceptsFile(final VirtualFile file) {
final ClsStubBuilderFactory[] factories = Extensions.getExtensions(ClsStubBuilderFactory.EP_NAME);
for (ClsStubBuilderFactory factory : factories) {
if (!factory.isInnerClass(file)) return true;
}
return false;
return true;
}
@Override
@@ -42,7 +39,8 @@ public class ClassFileStubBuilder implements BinaryFileStubBuilder {
final ClsStubBuilderFactory[] factories = Extensions.getExtensions(ClsStubBuilderFactory.EP_NAME);
for (ClsStubBuilderFactory factory : factories) {
if (!factory.isInnerClass(file) && factory.canBeProcessed(file, content)) {
return factory.buildFileStub(file, content);
PsiFileStub stub = factory.buildFileStub(file, content);
if (stub != null) return stub;
}
}
return null;
@@ -56,4 +54,4 @@ public class ClassFileStubBuilder implements BinaryFileStubBuilder {
public int getStubVersion() {
return JavaFileElementType.STUB_VERSION + 3;
}
}
}
@@ -40,9 +40,15 @@ public class DefaultClsStubBuilderFactory extends ClsStubBuilderFactory {
final PsiJavaFileStubImpl file = new PsiJavaFileStubImpl("do.not.know.yet", true);
try {
final ClassReader reader = new ClassReader(bytes);
final StubBuildingVisitor<VirtualFile> classVisitor =
new StubBuildingVisitor<VirtualFile>(vFile, VirtualFileInnerClassStrategy.INSTANCE, file, 0);
reader.accept(classVisitor, ClassReader.SKIP_FRAMES);
try {
reader.accept(classVisitor, ClassReader.SKIP_FRAMES);
}
catch (OutOfOrderInnerClassException e) {
return null;
}
@SuppressWarnings("unchecked") final PsiClassStub<PsiClass> result = (PsiClassStub<PsiClass>)classVisitor.getResult();
if (result == null) return null;
@@ -63,40 +69,16 @@ public class DefaultClsStubBuilderFactory extends ClsStubBuilderFactory {
@Override
public boolean isInnerClass(VirtualFile file) {
return isInner(file.getNameWithoutExtension(), new ParentDirectory(file));
}
String name = file.getNameWithoutExtension();
int len = name.length();
int idx = name.indexOf('$');
static boolean isInner(final String name, final Directory directory) {
return isInner(name, 0, directory);
}
private static boolean isInner(final String name, final int from, final Directory directory) {
final int index = name.indexOf('$', from);
return index != -1 && (containsPart(directory, name, index) || isInner(name, index + 1, directory));
}
private static boolean containsPart(Directory directory, String name, int endIndex) {
return endIndex > 0 && directory.contains(name.substring(0, endIndex));
}
interface Directory {
boolean contains(String name);
}
private static class ParentDirectory implements Directory {
private final VirtualFile myDirectory;
private final String myExtension;
private ParentDirectory(final VirtualFile file) {
myDirectory = file.getParent();
myExtension = file.getExtension();
while (idx > 0) {
if (idx + 1 < len && Character.isDigit(name.charAt(idx + 1))) return true;
idx = name.indexOf('$', idx + 1);
}
@Override
public boolean contains(final String name) {
final String fullName = myExtension == null ? name : name + "." + myExtension;
return myDirectory != null && myDirectory.findChild(fullName) != null;
}
return false;
}
private static class VirtualFileInnerClassStrategy implements InnerClassSourceStrategy<VirtualFile> {
@@ -0,0 +1,28 @@
/*
* Copyright 2000-2012 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.
*/
/*
* @author max
*/
package com.intellij.psi.impl.compiled;
public class OutOfOrderInnerClassException extends RuntimeException {
@Override
public Throwable fillInStackTrace() {
return this;
}
}
@@ -275,6 +275,9 @@ public class StubBuildingVisitor<T> extends ClassVisitor {
@Override
public void visitOuterClass(final String owner, final String name, final String desc) {
if (myParent instanceof PsiFileStub) {
throw new OutOfOrderInnerClassException();
}
}
@Override
@@ -296,7 +299,16 @@ public class StubBuildingVisitor<T> extends ClassVisitor {
if ((access & Opcodes.ACC_SYNTHETIC) != 0) return;
if (!isCorrectName(innerName)) return;
if (innerName == null || outerName == null || !getClassName(outerName).equals(myResult.getQualifiedName())) return;
if (innerName == null || outerName == null) return;
if ((getClassName(outerName) + "." + innerName).equals(myResult.getQualifiedName())) {
// Our result is inner class
if (myParent instanceof PsiFileStub) {
throw new OutOfOrderInnerClassException();
}
}
if (!getClassName(outerName).equals(myResult.getQualifiedName())) return;
final T innerSource = myInnersStrategy.findInnerClass(innerName, mySource);
if (innerSource == null) return;
@@ -1,146 +0,0 @@
package com.intellij.psi.impl.compiled;
import com.intellij.util.containers.ContainerUtil;
import junit.framework.TestCase;
import java.util.HashSet;
import java.util.Set;
/**
* Pavel.Fatin, 18.02.2010
*/
public class ClassFileStubBuilderTest extends TestCase {
public void testOneFile() {
assertTopLevel("");
assertTopLevel("$");
assertTopLevel("$$");
assertTopLevel("$$$");
assertTopLevel("Foo");
assertTopLevel("Foo$");
assertTopLevel("$Foo");
assertTopLevel("$Foo$");
assertTopLevel("Foo$Bar");
assertTopLevel("$Foo$Bar");
assertTopLevel("Foo$Bar$");
assertTopLevel("$Foo$Bar$");
assertTopLevel("$$Foo$$Bar$$");
}
public void testWrongCase() {
assertTopLevel("Foo$Bar", "foo");
assertTopLevel("foo$Bar", "Foo");
}
public void testShortName() {
assertTopLevel("A");
assertInner("Foo$B", "Foo");
assertInner("A$Foo", "A");
assertInner("A$B", "A");
assertTopLevel("A$B", "A$");
assertTopLevel("A$B", "$B");
assertTopLevel("A$B", "B");
assertTopLevel("A$B", "$");
}
public void testShortNames() {
assertInner("A$B$C", "A");
assertInner("A$B$C", "A$B");
assertInner("A$B$C", "A", "A$B");
assertTopLevel("A$B$C", "A$");
assertTopLevel("A$B$C", "A$B$");
}
public void testEmptyFileName() {
assertTopLevel("$Foo", "");
}
public void testNoInnerName() {
//assertTopLevel("Foo$", "Foo");
//assertTopLevel("Foo$$", "Foo$");
// an exception to the rule (to hide Scala "objects")
assertInner("Foo$", "Foo");
assertInner("Foo$$", "Foo$");
}
public void testDollarsInName() {
assertInner("$Foo$Bar", "$Foo");
assertInner("Foo$$Bar", "Foo");
assertInner("Foo$$Bar", "Foo");
assertInner("$Foo$$Bar", "$Foo");
assertInner("$Foo$$Bar", "$Foo$");
}
public void testInner() {
assertInner("Foo$Bar", "Foo");
assertInner("Foo$bar", "Foo");
assertInner("foo$Bar", "foo");
assertInner("foo$bar", "foo");
}
public void testInnerAndNoise() {
assertInner("Foo$Bar", "Foo", "Foo$", "$Bar", "Bar");
}
public void testTopLevel() {
assertTopLevel("Foo$Bar");
assertTopLevel("Foo$Bar", "Foo$");
assertTopLevel("Foo$Bar", "$Bar");
assertTopLevel("Foo$Bar", "Bar");
assertTopLevel("Foo$Bar", "Foo$", "$Bar", "Bar");
}
public void testInners() {
assertInner("Foo$Bar$Moo", "Foo");
assertInner("Foo$Bar$Moo", "Foo$Bar");
assertInner("Foo$Bar$Moo", "Foo", "Foo$Bar");
}
public void testInnersAndNoise() {
assertInner("Foo$Bar$Moo", "Foo", "$Bar", "Bar", "$Moo", "Moo", "Bar$Moo", "$Bar$Moo");
assertInner("Foo$Bar$Moo", "Foo$Bar", "$Bar", "Bar", "$Moo", "Moo", "Bar$Moo", "$Bar$Moo");
assertInner("Foo$Bar$Moo", "Foo", "Foo$Bar", "$Bar", "Bar", "$Moo", "Moo", "Bar$Moo", "$Bar$Moo");
}
public void testTopLevels() {
assertTopLevel("Foo$Bar$Moo");
assertTopLevel("Foo$Bar$Moo", "$Bar");
assertTopLevel("Foo$Bar$Moo", "Bar");
assertTopLevel("Foo$Bar$Moo", "$Moo");
assertTopLevel("Foo$Bar$Moo", "Moo");
assertTopLevel("Foo$Bar$Moo", "Bar$Moo");
assertTopLevel("Foo$Bar$Moo", "$Bar$Moo");
assertTopLevel("Foo$Bar$Moo", "$Bar", "Bar", "$Moo", "Moo", "Bar$Moo", "$Bar$Moo");
}
private static void assertInner(String name, String... files) {
assertTrue("Class " + name + " must be identified as inner", isInner(name, files));
}
private static void assertTopLevel(String name, String... files) {
assertFalse("Class " + name + " must be identified as top-level", isInner(name, files));
}
private static boolean isInner(String name, String... files) {
Set<String> all = new HashSet<String>();
ContainerUtil.addAll(all, files);
all.add(name);
return DefaultClsStubBuilderFactory.isInner(name, new DirectoryMock(all));
}
private static class DirectoryMock implements DefaultClsStubBuilderFactory.Directory {
private Set<String> myFiles;
private DirectoryMock(Set<String> files) {
myFiles = files;
}
@Override
public boolean contains(String name) {
return myFiles.contains(name);
}
}
}