From 7b7fe4dd60bb4687deac742df1c1e91c6817b9eb Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Tue, 1 Mar 2011 20:24:14 +0300 Subject: [PATCH] Unresolved references highlighted in the editor. Resolve test added --- .../org/jetbrains/jet/lang/ErrorHandler.java | 11 +- .../jet/lang/JetSemanticServices.java | 4 + .../jet/lang/annotations/JetPsiChecker.java | 5 +- .../lang/resolve/ClassDescriptorResolver.java | 2 +- .../jetbrains/jet/lang/resolve/JetScope.java | 10 +- .../jet/lang/resolve/JetScopeAdapter.java | 10 +- .../jet/lang/resolve/JetScopeImpl.java | 10 +- .../jet/lang/resolve/ScopeWithReceiver.java | 10 +- .../jet/lang/resolve/SubstitutingScope.java | 10 +- .../jet/lang/resolve/TypeResolver.java | 11 +- .../jet/lang/resolve/WritableScope.java | 10 +- .../jetbrains/jet/lang/types/ErrorType.java | 10 +- .../jet/lang/types/JetTypeInferrer.java | 10 +- idea/testData/checker/Properties.jet | 3 +- idea/testData/resolve/Basic.jet | 20 ++- .../ExtensibleHighlightingTestCase.java | 109 ----------- .../jet/resolve/ExpectedResolveData.java | 150 ++++++++++++++++ .../resolve/ExtensibleResolveTestCase.java | 104 +++++++++++ .../jetbrains/jet/resolve/JetResolveTest.java | 169 +++++++++--------- .../jet/types/JetTypeCheckerTest.java | 6 +- 20 files changed, 424 insertions(+), 250 deletions(-) delete mode 100644 idea/tests/org/jetbrains/jet/checkers/ExtensibleHighlightingTestCase.java create mode 100644 idea/tests/org/jetbrains/jet/resolve/ExpectedResolveData.java create mode 100644 idea/tests/org/jetbrains/jet/resolve/ExtensibleResolveTestCase.java diff --git a/idea/src/org/jetbrains/jet/lang/ErrorHandler.java b/idea/src/org/jetbrains/jet/lang/ErrorHandler.java index ff49d10840b..5f8192aa4de 100644 --- a/idea/src/org/jetbrains/jet/lang/ErrorHandler.java +++ b/idea/src/org/jetbrains/jet/lang/ErrorHandler.java @@ -1,10 +1,19 @@ package org.jetbrains.jet.lang; +import org.jetbrains.jet.lang.psi.JetReferenceExpression; + /** * @author abreslav */ public class ErrorHandler { public static final ErrorHandler DO_NOTHING = new ErrorHandler(); - public static final ErrorHandler THROW_EXCEPTION = new ErrorHandler(); + public static final ErrorHandler THROW_EXCEPTION = new ErrorHandler() { + @Override + public void unresolvedReference(JetReferenceExpression referenceExpression) { + throw new IllegalStateException("Unresolved reference: " + referenceExpression.getReferencedName()); + } + }; + public void unresolvedReference(JetReferenceExpression referenceExpression) { + } } diff --git a/idea/src/org/jetbrains/jet/lang/JetSemanticServices.java b/idea/src/org/jetbrains/jet/lang/JetSemanticServices.java index 037bdf90280..1ad60a093e3 100644 --- a/idea/src/org/jetbrains/jet/lang/JetSemanticServices.java +++ b/idea/src/org/jetbrains/jet/lang/JetSemanticServices.java @@ -44,4 +44,8 @@ public class JetSemanticServices { public JetTypeInferrer getTypeInferrer(BindingTrace trace) { return new JetTypeInferrer(trace, this); } + + public ErrorHandler getErrorHandler() { + return errorHandler; + } } diff --git a/idea/src/org/jetbrains/jet/lang/annotations/JetPsiChecker.java b/idea/src/org/jetbrains/jet/lang/annotations/JetPsiChecker.java index 28d65e16818..04810e38010 100644 --- a/idea/src/org/jetbrains/jet/lang/annotations/JetPsiChecker.java +++ b/idea/src/org/jetbrains/jet/lang/annotations/JetPsiChecker.java @@ -22,7 +22,10 @@ public class JetPsiChecker implements Annotator { if (element instanceof JetFile) { JetFile file = (JetFile) element; JetSemanticServices semanticServices = JetSemanticServices.createSemanticServices(element.getProject(), new ErrorHandler() { - + @Override + public void unresolvedReference(JetReferenceExpression referenceExpression) { + holder.createErrorAnnotation(referenceExpression, "Unresolved"); + } }); try { final BindingContext bindingContext = new TopDownAnalyzer(semanticServices).process(semanticServices.getStandardLibrary().getLibraryScope(), file.getRootNamespace().getDeclarations()); diff --git a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java index b7850af8d59..48dd34d8720 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java @@ -20,7 +20,7 @@ public class ClassDescriptorResolver { public ClassDescriptorResolver(JetSemanticServices semanticServices, BindingTrace trace) { this.semanticServices = semanticServices; - this.typeResolver = new TypeResolver(trace); + this.typeResolver = new TypeResolver(trace, semanticServices.getErrorHandler()); this.trace = trace; } diff --git a/idea/src/org/jetbrains/jet/lang/resolve/JetScope.java b/idea/src/org/jetbrains/jet/lang/resolve/JetScope.java index bf1b26d096e..ea0bdf54f23 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/JetScope.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/JetScope.java @@ -11,19 +11,19 @@ public interface JetScope { JetScope EMPTY = new JetScopeImpl() {}; @Nullable - ClassDescriptor getClass(String name); + ClassDescriptor getClass(@NotNull String name); @Nullable - PropertyDescriptor getProperty(String name); + PropertyDescriptor getProperty(@NotNull String name); @Nullable - ExtensionDescriptor getExtension(String name); + ExtensionDescriptor getExtension(@NotNull String name); @Nullable - NamespaceDescriptor getNamespace(String name); + NamespaceDescriptor getNamespace(@NotNull String name); @Nullable - TypeParameterDescriptor getTypeParameter(String name); + TypeParameterDescriptor getTypeParameter(@NotNull String name); @NotNull Type getThisType(); diff --git a/idea/src/org/jetbrains/jet/lang/resolve/JetScopeAdapter.java b/idea/src/org/jetbrains/jet/lang/resolve/JetScopeAdapter.java index 8a6d56c9117..39ade6dbdb8 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/JetScopeAdapter.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/JetScopeAdapter.java @@ -26,27 +26,27 @@ public class JetScopeAdapter implements JetScope { } @Override - public TypeParameterDescriptor getTypeParameter(String name) { + public TypeParameterDescriptor getTypeParameter(@NotNull String name) { return scope.getTypeParameter(name); } @Override - public NamespaceDescriptor getNamespace(String name) { + public NamespaceDescriptor getNamespace(@NotNull String name) { return scope.getNamespace(name); } @Override - public ClassDescriptor getClass(String name) { + public ClassDescriptor getClass(@NotNull String name) { return scope.getClass(name); } @Override - public PropertyDescriptor getProperty(String name) { + public PropertyDescriptor getProperty(@NotNull String name) { return scope.getProperty(name); } @Override - public ExtensionDescriptor getExtension(String name) { + public ExtensionDescriptor getExtension(@NotNull String name) { return scope.getExtension(name); } diff --git a/idea/src/org/jetbrains/jet/lang/resolve/JetScopeImpl.java b/idea/src/org/jetbrains/jet/lang/resolve/JetScopeImpl.java index 5ee66446eec..98777dc0ea5 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/JetScopeImpl.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/JetScopeImpl.java @@ -8,27 +8,27 @@ import org.jetbrains.jet.lang.types.*; */ public abstract class JetScopeImpl implements JetScope { @Override - public ClassDescriptor getClass(String name) { + public ClassDescriptor getClass(@NotNull String name) { return null; } @Override - public PropertyDescriptor getProperty(String name) { + public PropertyDescriptor getProperty(@NotNull String name) { return null; } @Override - public ExtensionDescriptor getExtension(String name) { + public ExtensionDescriptor getExtension(@NotNull String name) { return null; } @Override - public NamespaceDescriptor getNamespace(String name) { + public NamespaceDescriptor getNamespace(@NotNull String name) { return null; } @Override - public TypeParameterDescriptor getTypeParameter(String name) { + public TypeParameterDescriptor getTypeParameter(@NotNull String name) { return null; } diff --git a/idea/src/org/jetbrains/jet/lang/resolve/ScopeWithReceiver.java b/idea/src/org/jetbrains/jet/lang/resolve/ScopeWithReceiver.java index c40355a27f8..af1ab449f65 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/ScopeWithReceiver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/ScopeWithReceiver.java @@ -17,28 +17,28 @@ public class ScopeWithReceiver extends JetScopeImpl { } @Override - public ClassDescriptor getClass(String name) { + public ClassDescriptor getClass(@NotNull String name) { return super.getClass(name); // TODO } @Override - public PropertyDescriptor getProperty(String name) { + public PropertyDescriptor getProperty(@NotNull String name) { return receiverTypeScope.getProperty(name); // TODO : extension properties } @Override - public ExtensionDescriptor getExtension(String name) { + public ExtensionDescriptor getExtension(@NotNull String name) { return super.getExtension(name); // TODO } @Override - public NamespaceDescriptor getNamespace(String name) { + public NamespaceDescriptor getNamespace(@NotNull String name) { return outerScope.getNamespace(name); } @Override - public TypeParameterDescriptor getTypeParameter(String name) { + public TypeParameterDescriptor getTypeParameter(@NotNull String name) { return outerScope.getTypeParameter(name); } diff --git a/idea/src/org/jetbrains/jet/lang/resolve/SubstitutingScope.java b/idea/src/org/jetbrains/jet/lang/resolve/SubstitutingScope.java index 5194eeb5e5c..6b89c581740 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/SubstitutingScope.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/SubstitutingScope.java @@ -19,7 +19,7 @@ public class SubstitutingScope implements JetScope { } @Override - public PropertyDescriptor getProperty(String name) { + public PropertyDescriptor getProperty(@NotNull String name) { PropertyDescriptor property = workerScope.getProperty(name); if (property == null || substitutionContext.isEmpty()) { return property; @@ -28,7 +28,7 @@ public class SubstitutingScope implements JetScope { } @Override - public ClassDescriptor getClass(String name) { + public ClassDescriptor getClass(@NotNull String name) { ClassDescriptor descriptor = workerScope.getClass(name); if (descriptor == null) { return null; @@ -37,17 +37,17 @@ public class SubstitutingScope implements JetScope { } @Override - public ExtensionDescriptor getExtension(String name) { + public ExtensionDescriptor getExtension(@NotNull String name) { throw new UnsupportedOperationException(); // TODO } @Override - public NamespaceDescriptor getNamespace(String name) { + public NamespaceDescriptor getNamespace(@NotNull String name) { throw new UnsupportedOperationException(); // TODO } @Override - public TypeParameterDescriptor getTypeParameter(String name) { + public TypeParameterDescriptor getTypeParameter(@NotNull String name) { throw new UnsupportedOperationException(); // TODO } diff --git a/idea/src/org/jetbrains/jet/lang/resolve/TypeResolver.java b/idea/src/org/jetbrains/jet/lang/resolve/TypeResolver.java index d20c05abecc..d893c253465 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/TypeResolver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/TypeResolver.java @@ -2,6 +2,7 @@ package org.jetbrains.jet.lang.resolve; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.ErrorHandler; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.types.*; @@ -16,9 +17,11 @@ import java.util.Set; public class TypeResolver { private final BindingTrace trace; + private final ErrorHandler errorHandler; - public TypeResolver(BindingTrace trace) { + public TypeResolver(BindingTrace trace, ErrorHandler errorHandler) { this.trace = trace; + this.errorHandler = errorHandler; } @NotNull @@ -52,6 +55,7 @@ public class TypeResolver { else if (type.getTypeArguments().isEmpty()) { TypeParameterDescriptor typeParameterDescriptor = scope.getTypeParameter(type.getReferencedName()); if (typeParameterDescriptor != null) { + trace.recordReferenceResolution(type.getReferenceExpression(), typeParameterDescriptor); result[0] = new TypeImpl( attributes, typeParameterDescriptor.getTypeConstructor(), @@ -60,8 +64,13 @@ public class TypeResolver { // TODO : joint domain JetStandardClasses.STUB ); + } else { + errorHandler.unresolvedReference(type.getReferenceExpression()); } } + else { + errorHandler.unresolvedReference(type.getReferenceExpression()); + } } @Override diff --git a/idea/src/org/jetbrains/jet/lang/resolve/WritableScope.java b/idea/src/org/jetbrains/jet/lang/resolve/WritableScope.java index 83bcc735646..ce10ea0472e 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/WritableScope.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/WritableScope.java @@ -43,7 +43,7 @@ public class WritableScope extends JetScopeAdapter { } @Override - public PropertyDescriptor getProperty(String name) { + public PropertyDescriptor getProperty(@NotNull String name) { @NotNull Map propertyDescriptors = getPropertyDescriptors(); PropertyDescriptor propertyDescriptor = propertyDescriptors.get(name); @@ -102,7 +102,7 @@ public class WritableScope extends JetScopeAdapter { } @Override - public TypeParameterDescriptor getTypeParameter(String name) { + public TypeParameterDescriptor getTypeParameter(@NotNull String name) { TypeParameterDescriptor typeParameterDescriptor = getTypeParameterDescriptors().get(name); if (typeParameterDescriptor != null) { return typeParameterDescriptor; @@ -130,7 +130,7 @@ public class WritableScope extends JetScopeAdapter { } @Override - public ClassDescriptor getClass(String name) { + public ClassDescriptor getClass(@NotNull String name) { ClassDescriptor classDescriptor = getClassDescriptors().get(name); if (classDescriptor != null) { return classDescriptor; @@ -148,12 +148,12 @@ public class WritableScope extends JetScopeAdapter { } @Override - public NamespaceDescriptor getNamespace(String name) { + public NamespaceDescriptor getNamespace(@NotNull String name) { return super.getNamespace(name); // TODO } @Override - public ExtensionDescriptor getExtension(String name) { + public ExtensionDescriptor getExtension(@NotNull String name) { return super.getExtension(name); // TODO } diff --git a/idea/src/org/jetbrains/jet/lang/types/ErrorType.java b/idea/src/org/jetbrains/jet/lang/types/ErrorType.java index 21b9c0b01e9..e26f595c8e9 100644 --- a/idea/src/org/jetbrains/jet/lang/types/ErrorType.java +++ b/idea/src/org/jetbrains/jet/lang/types/ErrorType.java @@ -12,27 +12,27 @@ import java.util.List; public class ErrorType { private static final JetScope ERROR_SCOPE = new JetScope() { @Override - public ClassDescriptor getClass(String name) { + public ClassDescriptor getClass(@NotNull String name) { throw new UnsupportedOperationException(); // TODO } @Override - public PropertyDescriptor getProperty(String name) { + public PropertyDescriptor getProperty(@NotNull String name) { throw new UnsupportedOperationException(); // TODO } @Override - public ExtensionDescriptor getExtension(String name) { + public ExtensionDescriptor getExtension(@NotNull String name) { throw new UnsupportedOperationException(); // TODO } @Override - public NamespaceDescriptor getNamespace(String name) { + public NamespaceDescriptor getNamespace(@NotNull String name) { throw new UnsupportedOperationException(); // TODO } @Override - public TypeParameterDescriptor getTypeParameter(String name) { + public TypeParameterDescriptor getTypeParameter(@NotNull String name) { throw new UnsupportedOperationException(); // TODO } diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java index b2e68409072..c77f0308f71 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java @@ -24,7 +24,7 @@ public class JetTypeInferrer { public JetTypeInferrer(BindingTrace trace, JetSemanticServices semanticServices) { this.trace = trace; this.semanticServices = semanticServices; - this.typeResolver = new TypeResolver(trace); + this.typeResolver = new TypeResolver(trace, semanticServices.getErrorHandler()); this.classDescriptorResolver = new ClassDescriptorResolver(semanticServices, trace); } @@ -50,9 +50,11 @@ public class JetTypeInferrer { // TODO : other members // TODO : type substitutions??? PropertyDescriptor property = scope.getProperty(expression.getReferencedName()); - trace.recordReferenceResolution(expression, property); if (property != null) { + trace.recordReferenceResolution(expression, property); result[0] = property.getType(); + } else { + semanticServices.getErrorHandler().unresolvedReference(expression); } } @@ -400,6 +402,8 @@ public class JetTypeInferrer { FunctionDescriptor descriptor = result[0].getFunctionDescriptorForNamedArguments(typeArguments, valueArgumentTypes, functionLiteralArgumentType); if (descriptor != null) { trace.recordReferenceResolution(reference[0], descriptor); + } else { + semanticServices.getErrorHandler().unresolvedReference(reference[0]); } return descriptor; } @@ -409,6 +413,8 @@ public class JetTypeInferrer { FunctionDescriptor descriptor = result[0].getFunctionDescriptorForPositionedArguments(typeArguments, positionedValueArgumentTypes); if (descriptor != null) { trace.recordReferenceResolution(reference[0], descriptor); + } else { + semanticServices.getErrorHandler().unresolvedReference(reference[0]); } return descriptor; } diff --git a/idea/testData/checker/Properties.jet b/idea/testData/checker/Properties.jet index 04793824d60..a4ad01200e6 100644 --- a/idea/testData/checker/Properties.jet +++ b/idea/testData/checker/Properties.jet @@ -1,2 +1 @@ -val foo = 1 - +val x : Int \ No newline at end of file diff --git a/idea/testData/resolve/Basic.jet b/idea/testData/resolve/Basic.jet index ab749fb025e..53556723de2 100644 --- a/idea/testData/resolve/Basic.jet +++ b/idea/testData/resolve/Basic.jet @@ -1,17 +1,19 @@ -class A { - class B { +~A~class A { + ~B~class B { } - fun foo(a : Int) = a - fun fooB() = foo(1) - fun foo() : Int = 1.plus(1) - fun foo1() : B = new B() - val a : Int + ~foo~fun foo(~foo.a~a : `std::Char`Char) = `foo.a`a + ~fooB~fun fooB() = `foo`foo('1') + ~foo.1~fun foo() : Int = 1.`std::Int.plus(Int)`plus(1) + ~foo1~fun foo1() : `B`B = new `B`B() + ~A.a~val a : `std::Int`Int } -class C : A { - class B : C { +~C~class C : `A`A { + ~C.B~class B : `C`C { } + + val x : `C.B`B } diff --git a/idea/tests/org/jetbrains/jet/checkers/ExtensibleHighlightingTestCase.java b/idea/tests/org/jetbrains/jet/checkers/ExtensibleHighlightingTestCase.java deleted file mode 100644 index ace0c66c258..00000000000 --- a/idea/tests/org/jetbrains/jet/checkers/ExtensibleHighlightingTestCase.java +++ /dev/null @@ -1,109 +0,0 @@ -package org.jetbrains.jet.checkers; - -import com.intellij.codeHighlighting.Pass; -import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; -import com.intellij.codeInsight.daemon.DaemonCodeAnalyzerSettings; -import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl; -import com.intellij.codeInsight.daemon.impl.HighlightInfo; -import com.intellij.injected.editor.EditorWindow; -import com.intellij.openapi.command.CommandProcessor; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.vfs.VirtualFileFilter; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiFile; -import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; -import com.intellij.testFramework.ExpectedHighlightingData; -import com.intellij.testFramework.FileTreeAccessFilter; -import com.intellij.testFramework.LightCodeInsightTestCase; -import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl; -import com.intellij.util.ArrayUtil; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; - -import java.util.Collection; -import java.util.List; - -/** - * @author abreslav - */ -public abstract class ExtensibleHighlightingTestCase extends LightCodeInsightTestCase { - private final FileTreeAccessFilter myJavaFilesFilter = new FileTreeAccessFilter(); - - @Override - protected void setUp() throws Exception { - super.setUp(); - ((DaemonCodeAnalyzerImpl) DaemonCodeAnalyzer.getInstance(getProject())).prepareForTest(true); - DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(false); - } - - @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 - super.tearDown(); - } - - @Override - protected void runTest() throws Throwable { - final Throwable[] throwable = {null}; - CommandProcessor.getInstance().executeCommand(getProject(), new Runnable() { - @Override - public void run() { - try { - doRunTest(); - } - catch (Throwable t) { - throwable[0] = t; - } - } - }, "", null); - if (throwable[0] != null) { - throw throwable[0]; - } - } - - protected void doTest(@NonNls String filePath, boolean checkWarnings, boolean checkInfos) throws Exception { - configureByFile(filePath); - doTestConfiguredFile(checkWarnings, checkInfos); - } - - protected void doTestConfiguredFile(boolean checkWarnings, boolean checkInfos) { - getJavaFacade().setAssertOnFileLoadingFilter(VirtualFileFilter.NONE); - - ExpectedHighlightingData expectedData = new ExpectedHighlightingData(getEditor().getDocument(),checkWarnings, checkInfos) { - @Override - protected void initAdditionalHighlightingTypes() { -// this.this.put(); - } - }; - - PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); - getFile().getText(); //to load text - myJavaFilesFilter.allowTreeAccessForFile(getVFile()); - getJavaFacade().setAssertOnFileLoadingFilter(myJavaFilesFilter); // check repository work - - Collection infos = doHighlighting(); - - getJavaFacade().setAssertOnFileLoadingFilter(VirtualFileFilter.NONE); - - expectedData.checkResult(infos, getEditor().getDocument().getText()); - } - - @NotNull - protected List doHighlighting() { - PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); - - int[] toIgnore = doFolding() ? ArrayUtil.EMPTY_INT_ARRAY : new int[]{Pass.UPDATE_FOLDING}; - Editor editor = getEditor(); - PsiFile file = getFile(); - if (editor instanceof EditorWindow) { - editor = ((EditorWindow)editor).getDelegate(); - file = InjectedLanguageUtil.getTopLevelFile(file); - } - - return CodeInsightTestFixtureImpl.instantiateAndRun(file, editor, toIgnore, false); - } - - protected boolean doFolding() { - return false; - } -} diff --git a/idea/tests/org/jetbrains/jet/resolve/ExpectedResolveData.java b/idea/tests/org/jetbrains/jet/resolve/ExpectedResolveData.java new file mode 100644 index 00000000000..1e21cdd8d67 --- /dev/null +++ b/idea/tests/org/jetbrains/jet/resolve/ExpectedResolveData.java @@ -0,0 +1,150 @@ +package org.jetbrains.jet.resolve; + +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.editor.Document; +import com.intellij.psi.PsiElement; +import org.jetbrains.jet.lang.ErrorHandler; +import org.jetbrains.jet.lang.JetSemanticServices; +import org.jetbrains.jet.lang.psi.JetDeclaration; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.psi.JetReferenceExpression; +import org.jetbrains.jet.lang.psi.JetTypeReference; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.TopDownAnalyzer; +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.regex.Matcher; +import java.util.regex.Pattern; + +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertSame; + +/** + * @author abreslav + */ +public class ExpectedResolveData { + + private final Map declarationToPosition = new HashMap(); + private final Map positionToReference = new HashMap(); + + public ExpectedResolveData(final Document document) { + new WriteCommandAction.Simple(null) { + public void run() { + extractData(document); + } + }.execute().throwException(); + } + + private void extractData(Document document) { + String text = document.getText(); + + Pattern pattern = Pattern.compile("(~[^~]+~)|(`[^`]+`)"); + while (true) { + Matcher matcher = pattern.matcher(text); + if (!matcher.find()) break; + + String group = matcher.group(); + String name = group.substring(1, group.length() - 1); + if (group.startsWith("~")) { + if (declarationToPosition.put(name, matcher.start()) != null) { + throw new IllegalArgumentException("Redeclaration: " + name); + } + } + else if (group.startsWith("`")) { + positionToReference.put(matcher.start(), name); + } + else { + throw new IllegalStateException(); + } + + document.replaceString(matcher.start(), matcher.end(), ""); + text = document.getText(); + } + } + + public void checkResult(JetFile file) { + JetSemanticServices semanticServices = JetSemanticServices.createSemanticServices(file.getProject(), ErrorHandler.THROW_EXCEPTION); + JetStandardLibrary lib = semanticServices.getStandardLibrary(); + Map nameToDescriptor = new HashMap(); + nameToDescriptor.put("std::Int.plus(Int)", standardFunction(lib.getInt(), "plus", lib.getIntType())); + + TopDownAnalyzer topDownAnalyzer = new TopDownAnalyzer(semanticServices); + BindingContext bindingContext = topDownAnalyzer.process(lib.getLibraryScope(), file.getRootNamespace().getDeclarations()); + + Map nameToDeclaration = new HashMap(); + + Map declarationToName = new HashMap(); + for (Map.Entry entry : declarationToPosition.entrySet()) { + String name = entry.getKey(); + Integer position = entry.getValue(); + PsiElement element = file.findElementAt(position); + + JetDeclaration ancestorOfType = getAncestorOfType(JetDeclaration.class, element); + nameToDeclaration.put(name, ancestorOfType); + declarationToName.put(ancestorOfType, name); + } + + for (Map.Entry entry : positionToReference.entrySet()) { + Integer position = entry.getKey(); + String name = entry.getValue(); + PsiElement element = file.findElementAt(position); + + JetDeclaration expected = nameToDeclaration.get(name); + + JetReferenceExpression reference = getAncestorOfType(JetReferenceExpression.class, element); + if (expected == null && name.startsWith("std::")) { + DeclarationDescriptor expectedDescriptor = nameToDescriptor.get(name); + JetTypeReference typeReference = getAncestorOfType(JetTypeReference.class, element); + if (expectedDescriptor != null) { + DeclarationDescriptor actual = bindingContext.resolveReferenceExpression(reference); + assertSame(expectedDescriptor, actual); + continue; + } + + Type actualType = bindingContext.resolveTypeReference(typeReference); + assertNotNull("Type " + name + " not resolved for reference " + name, actualType); + ClassDescriptor expectedClass = lib.getLibraryScope().getClass(name.substring(5)); + assertNotNull("Expected class not found: " + name); + assertSame("Type resolution mismatch: ", expectedClass.getTypeConstructor(), actualType.getConstructor()); + continue; + } + assert expected != null : "No declaration for " + name; + + PsiElement actual = bindingContext.resolveToDeclarationPsiElement(reference); + String actualName = null; + if (actual != null) { + actualName = declarationToName.get(actual); + if (actualName == null) { + actualName = actual.toString(); + } + } + assertSame( + "Reference `" + name + "`" + reference.getReferencedName() + " at " + reference.getTextOffset() + " is resolved into " + actualName + ".", + expected, actual); + } + } + + private DeclarationDescriptor standardFunction(ClassDescriptor classDescriptor, String name, Type parameterType) { + FunctionGroup functionGroup = classDescriptor.getMemberScope(Collections.emptyList()).getFunctionGroup(name); + Collection functions = functionGroup.getPossiblyApplicableFunctions(Collections.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 getAncestorOfType(Class type, PsiElement element) { + while (element != null && !type.isInstance(element)) { + element = element.getParent(); + } + @SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"}) + T result = (T) element; + return result; + } +} diff --git a/idea/tests/org/jetbrains/jet/resolve/ExtensibleResolveTestCase.java b/idea/tests/org/jetbrains/jet/resolve/ExtensibleResolveTestCase.java new file mode 100644 index 00000000000..4023520cb78 --- /dev/null +++ b/idea/tests/org/jetbrains/jet/resolve/ExtensibleResolveTestCase.java @@ -0,0 +1,104 @@ +package org.jetbrains.jet.resolve; + +import com.intellij.codeHighlighting.Pass; +import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; +import com.intellij.codeInsight.daemon.DaemonCodeAnalyzerSettings; +import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl; +import com.intellij.codeInsight.daemon.impl.HighlightInfo; +import com.intellij.injected.editor.EditorWindow; +import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.vfs.VirtualFileFilter; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; +import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; +import com.intellij.testFramework.FileTreeAccessFilter; +import com.intellij.testFramework.LightCodeInsightTestCase; +import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl; +import com.intellij.util.ArrayUtil; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetFile; + +import java.util.Collection; +import java.util.List; + +/** + * @author abreslav + */ +public abstract class ExtensibleResolveTestCase extends LightCodeInsightTestCase { + private final FileTreeAccessFilter myJavaFilesFilter = new FileTreeAccessFilter(); + + @Override + protected void setUp() throws Exception { + super.setUp(); + ((DaemonCodeAnalyzerImpl) DaemonCodeAnalyzer.getInstance(getProject())).prepareForTest(true); + DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(false); + } + + @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 + super.tearDown(); + } + + @Override + protected void runTest() throws Throwable { + final Throwable[] throwable = {null}; + CommandProcessor.getInstance().executeCommand(getProject(), new Runnable() { + @Override + public void run() { + try { + doRunTest(); + } catch (Throwable t) { + throwable[0] = t; + } + } + }, "", null); + if (throwable[0] != null) { + throw throwable[0]; + } + } + + protected void doTest(@NonNls String filePath, boolean checkWarnings, boolean checkInfos) throws Exception { + configureByFile(filePath); + doTestConfiguredFile(checkWarnings, checkInfos); + } + + protected void doTestConfiguredFile(boolean checkWarnings, boolean checkInfos) { + getJavaFacade().setAssertOnFileLoadingFilter(VirtualFileFilter.NONE); + +// ExpectedHighlightingData expectedData = new ExpectedHighlightingData(getEditor().getDocument(), checkWarnings, checkInfos); + ExpectedResolveData expectedData = new ExpectedResolveData(getEditor().getDocument()); + + PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); + getFile().getText(); //to load text + myJavaFilesFilter.allowTreeAccessForFile(getVFile()); + getJavaFacade().setAssertOnFileLoadingFilter(myJavaFilesFilter); // check repository work + + Collection infos = doHighlighting(); + + getJavaFacade().setAssertOnFileLoadingFilter(VirtualFileFilter.NONE); + + expectedData.checkResult((JetFile) getFile()); + } + + @NotNull + protected List doHighlighting() { + PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); + + int[] toIgnore = doFolding() ? ArrayUtil.EMPTY_INT_ARRAY : new int[]{Pass.UPDATE_FOLDING}; + Editor editor = getEditor(); + PsiFile file = getFile(); + if (editor instanceof EditorWindow) { + editor = ((EditorWindow) editor).getDelegate(); + file = InjectedLanguageUtil.getTopLevelFile(file); + } + + return CodeInsightTestFixtureImpl.instantiateAndRun(file, editor, toIgnore, false); + } + + protected boolean doFolding() { + return false; + } +} diff --git a/idea/tests/org/jetbrains/jet/resolve/JetResolveTest.java b/idea/tests/org/jetbrains/jet/resolve/JetResolveTest.java index 25a478e61aa..b650c7ef94b 100644 --- a/idea/tests/org/jetbrains/jet/resolve/JetResolveTest.java +++ b/idea/tests/org/jetbrains/jet/resolve/JetResolveTest.java @@ -1,25 +1,21 @@ package org.jetbrains.jet.resolve; -import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase; import com.intellij.openapi.application.PathManager; -import com.intellij.openapi.util.io.FileUtil; -import org.jetbrains.jet.lang.ErrorHandler; -import org.jetbrains.jet.lang.JetSemanticServices; -import org.jetbrains.jet.lang.psi.*; -import org.jetbrains.jet.lang.resolve.*; -import org.jetbrains.jet.lang.types.*; +import org.jetbrains.jet.lang.resolve.JetScope; +import org.jetbrains.jet.lang.resolve.OverloadDomain; +import org.jetbrains.jet.lang.resolve.OverloadResolver; +import org.jetbrains.jet.lang.types.FunctionDescriptor; +import org.jetbrains.jet.lang.types.JetStandardLibrary; +import org.jetbrains.jet.lang.types.Type; import org.jetbrains.jet.parsing.JetParsingTest; import java.io.File; -import java.io.FileReader; -import java.util.Collection; import java.util.Collections; -import java.util.List; /** * @author abreslav */ -public class JetResolveTest extends LightDaemonAnalyzerTestCase { +public class JetResolveTest extends ExtensibleResolveTestCase { private JetStandardLibrary library; @Override @@ -38,81 +34,82 @@ public class JetResolveTest extends LightDaemonAnalyzerTestCase { } public void testBasic() throws Exception { - JetFile jetFile = JetChangeUtil.createFile(getProject(), FileUtil.loadTextAndClose(new FileReader(getTestDataPath() + "/resolve/Basic.jet"))); - List declarations = jetFile.getRootNamespace().getDeclarations(); - BindingContext bindingContext = new TopDownAnalyzer(JetSemanticServices.createSemanticServices(library, ErrorHandler.THROW_EXCEPTION)).process(library.getLibraryScope(), declarations); - - JetClass classADecl = (JetClass) declarations.get(0); - ClassDescriptor classA = bindingContext.getClassDescriptor(classADecl); - assertNotNull(classA); - - JetScope membersOfA = classA.getMemberScope(Collections.emptyList()); - ClassDescriptor classB = membersOfA.getClass("B"); - assertNotNull(classB); - - { - FunctionGroup fooFG = membersOfA.getFunctionGroup("foo"); - assertFalse(fooFG.isEmpty()); - } - - assertReturnType(membersOfA, "foo", library.getIntType()); - assertReturnType(membersOfA, "foo1", new TypeImpl(classB)); - assertReturnType(membersOfA, "fooB", library.getIntType()); - - JetFunction fooDecl = (JetFunction) classADecl.getDeclarations().get(1); - Type expressionType = bindingContext.getExpressionType(fooDecl.getBodyExpression()); - assertEquals(library.getIntType(), expressionType); - - { - DeclarationDescriptor resolve = bindingContext.resolveReferenceExpression((JetReferenceExpression) fooDecl.getBodyExpression()); - assertSame(bindingContext.getFunctionDescriptor(fooDecl).getUnsubstitutedValueParameters().get(0), resolve); - } - - { - JetFunction fooBDecl = (JetFunction) classADecl.getDeclarations().get(2); - JetCallExpression fooBBody = (JetCallExpression) fooBDecl.getBodyExpression(); - JetReferenceExpression refToFoo = (JetReferenceExpression) fooBBody.getCalleeExpression(); - FunctionDescriptor mustBeFoo = (FunctionDescriptor) bindingContext.resolveReferenceExpression(refToFoo); - assertSame(bindingContext.getFunctionDescriptor(fooDecl), FunctionDescriptorUtil.getOriginal(mustBeFoo)); - } - - { - JetFunction fooIntDecl = (JetFunction) classADecl.getDeclarations().get(3); - JetCallExpression fooIntBody = (JetCallExpression) fooIntDecl.getBodyExpression(); - JetDotQualifiedExpression qualifiedPlus = (JetDotQualifiedExpression) fooIntBody.getCalleeExpression(); - JetReferenceExpression refToPlus = (JetReferenceExpression) qualifiedPlus.getSelectorExpression(); - FunctionDescriptor mustBePlus = (FunctionDescriptor) bindingContext.resolveReferenceExpression(refToPlus); - FunctionGroup plusGroup = library.getInt().getMemberScope(Collections.emptyList()).getFunctionGroup("plus"); - Collection pluses = plusGroup.getPossiblyApplicableFunctions(Collections.emptyList(), Collections.singletonList(library.getIntType())); - FunctionDescriptor intPlus = null; - for (FunctionDescriptor plus : pluses) { - intPlus = plus; - } - assertSame(intPlus, FunctionDescriptorUtil.getOriginal(mustBePlus)); - } - - { - PropertyDescriptor a = classA.getMemberScope(Collections.emptyList()).getProperty("a"); - JetProperty aDecl = (JetProperty) classADecl.getDeclarations().get(5); - PropertyDescriptor mustBeA = bindingContext.getPropertyDescriptor(aDecl); - assertSame(a, mustBeA); - - JetTypeReference propertyTypeRef = aDecl.getPropertyTypeRef(); - Type type = bindingContext.resolveTypeReference(propertyTypeRef); - assertEquals(library.getIntType(), type); - } - - JetClass classCDecl = (JetClass) declarations.get(1); - ClassDescriptor classC = bindingContext.getClassDescriptor(classCDecl); - assertNotNull(classC); - assertEquals(1, classC.getTypeConstructor().getSupertypes().size()); - assertEquals(classA.getTypeConstructor(), classC.getTypeConstructor().getSupertypes().iterator().next().getConstructor()); - - JetScope cScope = classC.getMemberScope(Collections.emptyList()); - ClassDescriptor classC_B = cScope.getClass("B"); - assertNotNull(classC_B); - assertNotSame(classC_B, classB); - assertEquals(classC.getTypeConstructor(), classC_B.getTypeConstructor().getSupertypes().iterator().next().getConstructor()); + doTest("/resolve/Basic.jet", true, true); +// JetFile jetFile = JetChangeUtil.createFile(getProject(), FileUtil.loadTextAndClose(new FileReader(getTestDataPath() + "/resolve/Basic.jet"))); +// List declarations = jetFile.getRootNamespace().getDeclarations(); +// BindingContext bindingContext = new TopDownAnalyzer(JetSemanticServices.createSemanticServices(library, ErrorHandler.THROW_EXCEPTION)).process(library.getLibraryScope(), declarations); +// +// JetClass classADecl = (JetClass) declarations.get(0); +// ClassDescriptor classA = bindingContext.getClassDescriptor(classADecl); +// assertNotNull(classA); +// +// JetScope membersOfA = classA.getMemberScope(Collections.emptyList()); +// ClassDescriptor classB = membersOfA.getClass("B"); +// assertNotNull(classB); +// +// { +// FunctionGroup fooFG = membersOfA.getFunctionGroup("foo"); +// assertFalse(fooFG.isEmpty()); +// } +// +// assertReturnType(membersOfA, "foo", library.getIntType()); +// assertReturnType(membersOfA, "foo1", new TypeImpl(classB)); +// assertReturnType(membersOfA, "fooB", library.getIntType()); +// +// JetFunction fooDecl = (JetFunction) classADecl.getDeclarations().get(1); +// Type expressionType = bindingContext.getExpressionType(fooDecl.getBodyExpression()); +// assertEquals(library.getIntType(), expressionType); +// +// { +// DeclarationDescriptor resolve = bindingContext.resolveReferenceExpression((JetReferenceExpression) fooDecl.getBodyExpression()); +// assertSame(bindingContext.getFunctionDescriptor(fooDecl).getUnsubstitutedValueParameters().get(0), resolve); +// } +// +// { +// JetFunction fooBDecl = (JetFunction) classADecl.getDeclarations().get(2); +// JetCallExpression fooBBody = (JetCallExpression) fooBDecl.getBodyExpression(); +// JetReferenceExpression refToFoo = (JetReferenceExpression) fooBBody.getCalleeExpression(); +// FunctionDescriptor mustBeFoo = (FunctionDescriptor) bindingContext.resolveReferenceExpression(refToFoo); +// assertSame(bindingContext.getFunctionDescriptor(fooDecl), FunctionDescriptorUtil.getOriginal(mustBeFoo)); +// } +// +// { +// JetFunction fooIntDecl = (JetFunction) classADecl.getDeclarations().get(3); +// JetCallExpression fooIntBody = (JetCallExpression) fooIntDecl.getBodyExpression(); +// JetDotQualifiedExpression qualifiedPlus = (JetDotQualifiedExpression) fooIntBody.getCalleeExpression(); +// JetReferenceExpression refToPlus = (JetReferenceExpression) qualifiedPlus.getSelectorExpression(); +// FunctionDescriptor mustBePlus = (FunctionDescriptor) bindingContext.resolveReferenceExpression(refToPlus); +// FunctionGroup plusGroup = library.getInt().getMemberScope(Collections.emptyList()).getFunctionGroup("plus"); +// Collection pluses = plusGroup.getPossiblyApplicableFunctions(Collections.emptyList(), Collections.singletonList(library.getIntType())); +// FunctionDescriptor intPlus = null; +// for (FunctionDescriptor plus : pluses) { +// intPlus = plus; +// } +// assertSame(intPlus, FunctionDescriptorUtil.getOriginal(mustBePlus)); +// } +// +// { +// PropertyDescriptor a = classA.getMemberScope(Collections.emptyList()).getProperty("a"); +// JetProperty aDecl = (JetProperty) classADecl.getDeclarations().get(5); +// PropertyDescriptor mustBeA = bindingContext.getPropertyDescriptor(aDecl); +// assertSame(a, mustBeA); +// +// JetTypeReference propertyTypeRef = aDecl.getPropertyTypeRef(); +// Type type = bindingContext.resolveTypeReference(propertyTypeRef); +// assertEquals(library.getIntType(), type); +// } +// +// JetClass classCDecl = (JetClass) declarations.get(1); +// ClassDescriptor classC = bindingContext.getClassDescriptor(classCDecl); +// assertNotNull(classC); +// assertEquals(1, classC.getTypeConstructor().getSupertypes().size()); +// assertEquals(classA.getTypeConstructor(), classC.getTypeConstructor().getSupertypes().iterator().next().getConstructor()); +// +// JetScope cScope = classC.getMemberScope(Collections.emptyList()); +// ClassDescriptor classC_B = cScope.getClass("B"); +// assertNotNull(classC_B); +// assertNotSame(classC_B, classB); +// assertEquals(classC.getTypeConstructor(), classC_B.getTypeConstructor().getSupertypes().iterator().next().getConstructor()); } private void assertReturnType(JetScope membersOfA, String foo, Type returnType) { diff --git a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java index 66f0d791f06..d6e7911e967 100644 --- a/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java +++ b/idea/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java @@ -33,7 +33,7 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { public void setUp() throws Exception { super.setUp(); library = new JetStandardLibrary(getProject()); - semanticServices = JetSemanticServices.createSemanticServices(library, ErrorHandler.THROW_EXCEPTION); + semanticServices = JetSemanticServices.createSemanticServices(library, ErrorHandler.DO_NOTHING); classDefinitions = new ClassDefinitions(); classDescriptorResolver = semanticServices.getClassDescriptorResolver(BindingTrace.DUMMY); } @@ -489,7 +489,7 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { } private static Type makeType(JetScope scope, String typeStr) { - return new TypeResolver(BindingTrace.DUMMY).resolveType(scope, JetChangeUtil.createType(getProject(), typeStr)); + return new TypeResolver(BindingTrace.DUMMY, ErrorHandler.THROW_EXCEPTION).resolveType(scope, JetChangeUtil.createType(getProject(), typeStr)); } private class ClassDefinitions { @@ -522,7 +522,7 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase { public JetScope BASIC_SCOPE = new JetScopeAdapter(library.getLibraryScope()) { @Override - public ClassDescriptor getClass(String name) { + public ClassDescriptor getClass(@NotNull String name) { if (CLASSES.isEmpty()) { for (String classDeclaration : CLASS_DECLARATIONS) { JetClass classElement = JetChangeUtil.createClass(getProject(), classDeclaration);