提交 4d574402 编写于 作者: A Andrey Breslav

namespace-level functions supported + tests for resolve to Java

上级 db9c36ad
......@@ -9,7 +9,8 @@ import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.ErrorHandler;
import org.jetbrains.jet.lang.resolve.*;
import org.jetbrains.jet.lang.resolve.AnalyzingUtils;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lexer.JetTokens;
/**
......@@ -28,6 +29,11 @@ public class JetReferenceExpression extends JetExpression {
return node == null ? null : node.getText();
}
@Nullable @IfNotParsed
public PsiElement getReferencedNameElement() {
return findChildByType(REFERENCE_TOKENS);
}
@Override
public void accept(JetVisitor visitor) {
visitor.visitReferenceExpression(this);
......
......@@ -25,7 +25,7 @@ public class AnalyzingUtils {
BindingTraceContext bindingTraceContext = new BindingTraceContext();
JavaSemanticServices javaSemanticServices = new JavaSemanticServices(project, semanticServices, bindingTraceContext);
ScopeWithImports scope = new ScopeWithImports(semanticServices.getStandardLibrary().getLibraryScope());
WritableScope scope = new WritableScope(semanticServices.getStandardLibrary().getLibraryScope());
scope.importScope(new JavaPackageScope("", javaSemanticServices));
scope.importScope(new JavaPackageScope("java.lang", javaSemanticServices));
......
......@@ -135,7 +135,7 @@ public class ClassDescriptorResolver {
JetExpression bodyExpression = function.getBodyExpression();
assert bodyExpression != null : "No type, no body"; // TODO
// TODO : Recursion possible
returnType = semanticServices.getTypeInferrer().getType(parameterScope, bodyExpression, function.hasBlockBody());
returnType = semanticServices.getTypeInferrer().safeGetType(parameterScope, bodyExpression, function.hasBlockBody());
}
FunctionDescriptorImpl functionDescriptor = new FunctionDescriptorImpl(
......
package org.jetbrains.jet.lang.resolve;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.types.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author abreslav
*/
public class ScopeWithImports implements JetScope {
private final List<JetScope> imports = new ArrayList<JetScope>();
private final JetScope scope;
public ScopeWithImports(JetScope scope) {
this.scope = scope;
}
public void importScope(JetScope imported) {
imports.add(0, imported);
}
@Override
public ClassDescriptor getClass(@NotNull String name) {
ClassDescriptor descriptor = scope.getClass(name);
if (descriptor != null) return descriptor;
for (JetScope imported : imports) {
ClassDescriptor importedClass = imported.getClass(name);
if (importedClass != null) {
return importedClass;
}
}
return null;
}
@Override
public PropertyDescriptor getProperty(@NotNull String name) {
PropertyDescriptor descriptor = scope.getProperty(name);
if (descriptor != null) return descriptor;
for (JetScope imported : imports) {
PropertyDescriptor importedDescriptor = imported.getProperty(name);
if (importedDescriptor != null) {
return importedDescriptor;
}
}
return null;
}
@Override
public ExtensionDescriptor getExtension(@NotNull String name) {
throw new UnsupportedOperationException(); // TODO
}
@Override
public NamespaceDescriptor getNamespace(@NotNull String name) {
NamespaceDescriptor descriptor = scope.getNamespace(name);
if (descriptor != null) return descriptor;
for (JetScope imported : imports) {
NamespaceDescriptor importedDescriptor = imported.getNamespace(name);
if (importedDescriptor != null) {
return importedDescriptor;
}
}
return null;
}
@Override
public TypeParameterDescriptor getTypeParameter(@NotNull String name) {
return scope.getTypeParameter(name);
}
@NotNull
@Override
public Type getThisType() {
return scope.getThisType();
}
@NotNull
@Override
public FunctionGroup getFunctionGroup(@NotNull String name) {
return scope.getFunctionGroup(name); // TODO
}
}
......@@ -13,6 +13,7 @@ import java.util.*;
public class TopDownAnalyzer {
private final Map<JetClass, MutableClassDescriptor> classes = new LinkedHashMap<JetClass, MutableClassDescriptor>();
private final Map<JetNamespace, WritableScope> namespaceScopes = new LinkedHashMap<JetNamespace, WritableScope>();
private final Map<JetFunction, FunctionDescriptor> functions = new HashMap<JetFunction, FunctionDescriptor>();
private final Map<JetDeclaration, WritableScope> declaringScopes = new HashMap<JetDeclaration, WritableScope>();
......@@ -56,7 +57,8 @@ public class TopDownAnalyzer {
public void visitNamespace(JetNamespace namespace) {
List<JetImportDirective> importDirectives = namespace.getImportDirectives();
ScopeWithImports scopeWithImports = new ScopeWithImports(declaringScope);
WritableScope namespaceScope = new WritableScope(declaringScope);
namespaceScopes.put(namespace, namespaceScope);
for (JetImportDirective importDirective : importDirectives) {
if (importDirective.isAbsoluteInRootNamespace()) {
......@@ -64,15 +66,15 @@ public class TopDownAnalyzer {
}
if (importDirective.isAllUnder()) {
JetExpression importedReference = importDirective.getImportedReference();
Type type = semanticServices.getTypeInferrer(trace).getType(scopeWithImports, importedReference, false);
Type type = semanticServices.getTypeInferrer(trace).getType(namespaceScope, importedReference, false);
if (type != null) {
scopeWithImports.importScope(type.getMemberScope());
namespaceScope.importScope(type.getMemberScope());
}
} else {
throw new UnsupportedOperationException();
}
}
WritableScope namespaceScope = new WritableScope(scopeWithImports);
collectTypeDeclarators(namespaceScope, namespace.getDeclarations());
}
......@@ -138,7 +140,8 @@ public class TopDownAnalyzer {
@Override
public void visitNamespace(JetNamespace namespace) {
collectBehaviorDeclarators(declaringScope, namespace.getDeclarations());
WritableScope namespaceScope = namespaceScopes.get(namespace);
collectBehaviorDeclarators(namespaceScope, namespace.getDeclarations());
}
@Override
......
......@@ -180,7 +180,11 @@ public class TypeResolver {
return classDescriptor.getMemberScope(resolveTypeProjections(scope, classDescriptor.getTypeConstructor(), userType.getTypeArguments()));
}
return resolveNamespace(scope, userType).getMemberScope();
NamespaceDescriptor namespaceDescriptor = resolveNamespace(scope, userType);
if (namespaceDescriptor == null) {
return JetScope.EMPTY;
}
return namespaceDescriptor.getMemberScope();
}
@Nullable
......@@ -199,7 +203,9 @@ public class TypeResolver {
}
NamespaceDescriptor namespace = scope.getNamespace(userType.getReferencedName());
trace.recordReferenceResolution(userType.getReferenceExpression(), namespace);
if (namespace != null) {
trace.recordReferenceResolution(userType.getReferenceExpression(), namespace);
}
return namespace;
}
......
......@@ -4,7 +4,9 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.types.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
......@@ -21,11 +23,25 @@ public class WritableScope extends JetScopeAdapter {
private Map<String, ClassDescriptor> classDescriptors;
@Nullable
private Type thisType;
@Nullable
private List<JetScope> imports;
public WritableScope(JetScope scope) {
super(scope);
}
public void importScope(JetScope imported) {
getImports().add(0, imported);
}
@NotNull
private List<JetScope> getImports() {
if (imports == null) {
imports = new ArrayList<JetScope>();
}
return imports;
}
@NotNull
private Map<String, PropertyDescriptor> getPropertyDescriptors() {
if (propertyDescriptors == null) {
......@@ -50,7 +66,17 @@ public class WritableScope extends JetScopeAdapter {
if (propertyDescriptor != null) {
return propertyDescriptor;
}
return super.getProperty(name);
propertyDescriptor = super.getProperty(name);
if (propertyDescriptor != null) {
return propertyDescriptor;
}
for (JetScope imported : getImports()) {
PropertyDescriptor importedDescriptor = imported.getProperty(name);
if (importedDescriptor != null) {
return importedDescriptor;
}
}
return null;
}
@NotNull
......@@ -77,11 +103,20 @@ public class WritableScope extends JetScopeAdapter {
@Override
@NotNull
public FunctionGroup getFunctionGroup(@NotNull String name) {
WritableFunctionGroup functionGroup = getFunctionGroups().get(name);
FunctionGroup functionGroup = getFunctionGroups().get(name);
if (functionGroup != null && !functionGroup.isEmpty()) {
return functionGroup;
}
return super.getFunctionGroup(name);
// TODO : this logic is questionable
functionGroup = super.getFunctionGroup(name);
if (!functionGroup.isEmpty()) return functionGroup;
for (JetScope imported : getImports()) {
FunctionGroup importedDescriptor = imported.getFunctionGroup(name);
if (!importedDescriptor.isEmpty()) {
return importedDescriptor;
}
}
return functionGroup;
}
@NotNull
......@@ -132,10 +167,17 @@ public class WritableScope extends JetScopeAdapter {
@Override
public ClassDescriptor getClass(@NotNull String name) {
ClassDescriptor classDescriptor = getClassDescriptors().get(name);
if (classDescriptor != null) {
return classDescriptor;
if (classDescriptor != null) return classDescriptor;
classDescriptor = super.getClass(name);
if (classDescriptor != null) return classDescriptor;
for (JetScope imported : getImports()) {
ClassDescriptor importedClass = imported.getClass(name);
if (importedClass != null) {
return importedClass;
}
}
return super.getClass(name);
return null;
}
@NotNull
......@@ -149,7 +191,15 @@ public class WritableScope extends JetScopeAdapter {
@Override
public NamespaceDescriptor getNamespace(@NotNull String name) {
return super.getNamespace(name); // TODO
NamespaceDescriptor namespace = super.getNamespace(name);
if (namespace != null) return namespace;
for (JetScope imported : getImports()) {
NamespaceDescriptor importedDescriptor = imported.getNamespace(name);
if (importedDescriptor != null) {
return importedDescriptor;
}
}
return null;
}
@Override
......
......@@ -28,20 +28,16 @@ public class JetTypeInferrer {
this.classDescriptorResolver = new ClassDescriptorResolver(semanticServices, trace);
}
/*
: "new" constructorInvocation
: objectLiteral
: SimpleName
: "typeof" "(" expression ")"
: functionLiteral
@NotNull
public Type safeGetType(@NotNull final JetScope scope, @NotNull JetExpression expression, final boolean preferBlock) {
Type type = getType(scope, expression, preferBlock);
if (type != null) {
return type;
}
return ErrorType.createErrorType("Type for " + expression.getText());
}
: declaration
: "namespace" // for the root namespace
*/
@Nullable
public Type getType(@NotNull final JetScope scope, @NotNull JetExpression expression, final boolean preferBlock) {
final Type[] result = new Type[1];
expression.accept(new JetVisitor() {
......
import `java::java`java.*
import `java::java.util`util.*
fun foo(o : `java::java.lang.Object`Object, l : `java::java.util`util.`java::java.util.List`List) : `java::java.util.List`List {}
~A~class A {
fun f(a : `java::java.util`util.`java::java.util.List`List) {}
fun f(a : `java::java.util.List`List) {}
}
class B : `java::java.lang.Object`Object {
fun bar(~o~o : `java::java.lang.Object`Object) {
`java::java.lang.System`System.`java::java.lang.System.out`out.`java::java.io.PrintStream.print(Object)`print(`o`o)
}
fun f(a : `java::java.util`util.`java::java.util.List`List) {}
fun f(a : `java::java.util.List`List) {}
~foo~fun foo(abc : `A`A) : `A`A {
`java::java`java.`java::java.lang`lang.`java::java.lang.System`System.`java::java.lang.System.out`out.`java::java.io.PrintStream.print(Int)`print(1)
`java::java.util`util.`java::java.util.Collections`Collections.`java::java.util.Collections.emptyList()`emptyList()
`java::java`java.`!`Foo
`java::java.util.Collections`Collections.`java::java.util.Collections.emptyList()`emptyList()
}
fun barrr() : `std::Int`Int {
`foo`foo(new `A`A())
}
}
\ No newline at end of file
......@@ -10,15 +10,11 @@ import org.jetbrains.jet.lang.resolve.AnalyzingUtils;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.types.*;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertSame;
import static junit.framework.Assert.*;
/**
* @author abreslav
......@@ -28,16 +24,24 @@ public class ExpectedResolveData {
private final Map<String, Integer> declarationToPosition = new HashMap<String, Integer>();
private final Map<Integer, String> positionToReference = new HashMap<Integer, String>();
private final Map<Integer, String> positionToType = new HashMap<Integer, String>();
private final Map<String, DeclarationDescriptor> nameToDescriptor;
private final Map<String, PsiElement> nameToPsiElement;
public ExpectedResolveData(final Document document) {
public ExpectedResolveData(Map<String, DeclarationDescriptor> nameToDescriptor, Map<String, PsiElement> nameToPsiElement) {
this.nameToDescriptor = nameToDescriptor;
this.nameToPsiElement = nameToPsiElement;
}
public void extractData(final Document document) {
new WriteCommandAction.Simple(null) {
public void run() {
extractData(document);
doExtractData(document);
}
}.execute().throwException();
}
private void extractData(Document document) {
private void doExtractData(Document document) {
String text = document.getText();
Pattern pattern = Pattern.compile("(~[^~]+~)|(`[^`]+`)");
......@@ -71,12 +75,16 @@ public class ExpectedResolveData {
}
public void checkResult(JetFile file) {
JetSemanticServices semanticServices = JetSemanticServices.createSemanticServices(file.getProject(), ErrorHandler.THROW_EXCEPTION);
final Set<PsiElement> unresolvedReferences = new HashSet<PsiElement>();
JetSemanticServices semanticServices = JetSemanticServices.createSemanticServices(file.getProject(), new ErrorHandler() {
@Override
public void unresolvedReference(JetReferenceExpression referenceExpression) {
unresolvedReferences.add(referenceExpression.getReferencedNameElement());
}
});
JetStandardLibrary lib = semanticServices.getStandardLibrary();
Map<String, DeclarationDescriptor> nameToDescriptor = new HashMap<String, DeclarationDescriptor>();
nameToDescriptor.put("std::Int.plus(Int)", standardFunction(lib.getInt(), "plus", lib.getIntType()));
BindingContext bindingContext = AnalyzingUtils.analyzeFile(file, ErrorHandler.THROW_EXCEPTION);
BindingContext bindingContext = AnalyzingUtils.analyzeFile(file, semanticServices.getErrorHandler());
Map<String, JetDeclaration> nameToDeclaration = new HashMap<String, JetDeclaration>();
......@@ -96,7 +104,15 @@ public class ExpectedResolveData {
String name = entry.getValue();
PsiElement element = file.findElementAt(position);
JetDeclaration expected = nameToDeclaration.get(name);
if ("!".equals(name)) {
assertTrue("Must have been unresolved: " + element, unresolvedReferences.contains(element));
continue;
}
PsiElement expected = nameToDeclaration.get(name);
if (expected == null) {
expected = nameToPsiElement.get(name);
}
JetReferenceExpression reference = getAncestorOfType(JetReferenceExpression.class, element);
if (expected == null && name.startsWith("std::")) {
......@@ -158,17 +174,6 @@ public class ExpectedResolveData {
}
}
private DeclarationDescriptor standardFunction(ClassDescriptor classDescriptor, String name, Type parameterType) {
FunctionGroup functionGroup = classDescriptor.getMemberScope(Collections.<TypeProjection>emptyList()).getFunctionGroup(name);
Collection<FunctionDescriptor> functions = functionGroup.getPossiblyApplicableFunctions(Collections.<Type>emptyList(), Collections.singletonList(parameterType));
for (FunctionDescriptor function : functions) {
if (function.getUnsubstitutedValueParameters().get(0).getType().equals(parameterType)) {
return function;
}
}
throw new IllegalArgumentException("Not found: std::" + classDescriptor.getName() + "." + name + "(" + parameterType + ")");
}
private <T> T getAncestorOfType(Class<T> type, PsiElement element) {
while (element != null && !type.isInstance(element)) {
element = element.getParent();
......
......@@ -28,17 +28,21 @@ import java.util.List;
*/
public abstract class ExtensibleResolveTestCase extends LightCodeInsightTestCase {
private final FileTreeAccessFilter myJavaFilesFilter = new FileTreeAccessFilter();
private ExpectedResolveData expectedResolveData;
@Override
protected void setUp() throws Exception {
super.setUp();
expectedResolveData = getExpectedResolveData();
((DaemonCodeAnalyzerImpl) DaemonCodeAnalyzer.getInstance(getProject())).prepareForTest(true);
DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(false);
}
protected abstract ExpectedResolveData getExpectedResolveData();
@Override
protected void tearDown() throws Exception {
((DaemonCodeAnalyzerImpl) DaemonCodeAnalyzer.getInstance(getProject())).cleanupAfterTest(true); // has to cleanup by hand since light project does not get disposed any time soon
((DaemonCodeAnalyzerImpl) DaemonCodeAnalyzer.getInstance(getProject())).cleanupAfterTest(); // has to cleanup by hand since light project does not get disposed any time soon
super.tearDown();
}
......@@ -69,7 +73,7 @@ public abstract class ExtensibleResolveTestCase extends LightCodeInsightTestCase
getJavaFacade().setAssertOnFileLoadingFilter(VirtualFileFilter.NONE);
// ExpectedHighlightingData expectedData = new ExpectedHighlightingData(getEditor().getDocument(), checkWarnings, checkInfos);
ExpectedResolveData expectedData = new ExpectedResolveData(getEditor().getDocument());
expectedResolveData.extractData(getEditor().getDocument());
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
getFile().getText(); //to load text
......@@ -80,7 +84,7 @@ public abstract class ExtensibleResolveTestCase extends LightCodeInsightTestCase
getJavaFacade().setAssertOnFileLoadingFilter(VirtualFileFilter.NONE);
expectedData.checkResult((JetFile) getFile());
expectedResolveData.checkResult((JetFile) getFile());
}
@NotNull
......
package org.jetbrains.jet.resolve;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.impl.JavaSdkImpl;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.search.GlobalSearchScope;
import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.parsing.JetParsingTest;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;
/**
* @author abreslav
......@@ -11,8 +23,55 @@ import java.io.File;
public class JetResolveTest extends ExtensibleResolveTestCase {
@Override
public void setUp() throws Exception {
super.setUp();
protected ExpectedResolveData getExpectedResolveData() {
Project project = getProject();
JetStandardLibrary lib = JetStandardLibrary.getJetStandardLibrary(project);
Map<String, DeclarationDescriptor> nameToDescriptor = new HashMap<String, DeclarationDescriptor>();
nameToDescriptor.put("std::Int.plus(Int)", standardFunction(lib.getInt(), "plus", lib.getIntType()));
Map<String,PsiElement> nameToDeclaration = new HashMap<String, PsiElement>();
nameToDeclaration.put("java::java.util.Collections.emptyList()", findMethod(findClass(project, "java.util.Collections"), "emptyList"));
nameToDeclaration.put("java::java.util.Collections", findClass(project, "java.util.Collections"));
nameToDeclaration.put("java::java.util.List", findClass(project, "java.util.List"));
nameToDeclaration.put("java::java", findPackage(project, "java"));
nameToDeclaration.put("java::java.util", findPackage(project, "java.util"));
nameToDeclaration.put("java::java.lang", findPackage(project, "java.lang"));
nameToDeclaration.put("java::java.lang.Object", findClass(project, "java.lang.Object"));
nameToDeclaration.put("java::java.lang.System", findClass(project, "java.lang.System"));
PsiMethod[] methods = findClass(project, "java.io.PrintStream").findMethodsByName("print", true);
nameToDeclaration.put("java::java.io.PrintStream.print(Object)", methods[8]);
nameToDeclaration.put("java::java.io.PrintStream.print(Int)", methods[2]);
nameToDeclaration.put("java::java.lang.System.out", findClass(project, "java.lang.System").findFieldByName("out", true));
return new ExpectedResolveData(nameToDescriptor, nameToDeclaration);
}
private PsiElement findPackage(Project project, String qualifiedName) {
JavaPsiFacade javaFacade = JavaPsiFacade.getInstance(project);
return javaFacade.findPackage(qualifiedName);
}
private PsiMethod findMethod(PsiClass collections, String name) {
PsiMethod[] emptyLists = collections.findMethodsByName(name, true);
return emptyLists[0];
}
private PsiClass findClass(Project project, String qualifiedName) {
JavaPsiFacade javaFacade = JavaPsiFacade.getInstance(project);
GlobalSearchScope javaSearchScope = GlobalSearchScope.allScope(project);
return javaFacade.findClass(qualifiedName, javaSearchScope);
}
private DeclarationDescriptor standardFunction(ClassDescriptor classDescriptor, String name, Type parameterType) {
FunctionGroup functionGroup = classDescriptor.getMemberScope(Collections.<TypeProjection>emptyList()).getFunctionGroup(name);
Collection<FunctionDescriptor> functions = functionGroup.getPossiblyApplicableFunctions(Collections.<Type>emptyList(), Collections.singletonList(parameterType));
for (FunctionDescriptor function : functions) {
if (function.getUnsubstitutedValueParameters().get(0).getType().equals(parameterType)) {
return function;
}
}
throw new IllegalArgumentException("Not found: std::" + classDescriptor.getName() + "." + name + "(" + parameterType + ")");
}
@Override
......@@ -20,6 +79,18 @@ public class JetResolveTest extends ExtensibleResolveTestCase {
return getHomeDirectory() + "/idea/testData";
}
@Override
protected Sdk getProjectJDK() {
Properties properties = new Properties();
try {
properties.load(new FileReader(getHomeDirectory() + "/idea/idea.properties"));
} catch (IOException e) {
throw new RuntimeException(e);
}
String home = properties.getProperty("idea.home");
return new JavaSdkImpl().createJdk("JDK", home + "/java/mockJDK-1.7/jre", true);
}
private static String getHomeDirectory() {
return new File(PathManager.getResourceRoot(JetParsingTest.class, "/org/jetbrains/jet/parsing/JetParsingTest.class")).getParentFile().getParentFile().getParent();
}
......@@ -28,4 +99,8 @@ public class JetResolveTest extends ExtensibleResolveTestCase {
doTest("/resolve/Basic.jet", true, true);
}
public void testResolveToJava() throws Exception {
doTest("/resolve/ResolveToJava.jet", true, true);
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册