From 3588779f1945fca7478458efa90ceccb93df7340 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Tue, 1 Nov 2011 16:04:55 +0300 Subject: [PATCH] When a file is changed in the IDE, we do not recheck function bodies and other executable code in other files: only declarations to povide resolution context for the changed file --- .../org/jetbrains/jet/cli/CompileSession.java | 3 +- .../jet/lang/resolve/java/AnalyzerFacade.java | 3 + .../jet/lang/resolve/AnalyzingUtils.java | 16 +++-- .../jet/lang/resolve/BodyResolver.java | 63 ++++++++++--------- .../jet/lang/resolve/ControlFlowAnalyzer.java | 2 + .../jet/lang/resolve/DeclarationsChecker.java | 7 ++- .../lang/resolve/TopDownAnalysisContext.java | 39 +++++++++++- .../jet/lang/resolve/TopDownAnalyzer.java | 21 +++++-- .../lang/resolve/TypeHierarchyResolver.java | 2 +- .../jet/plugin/compiler/JetCompiler.java | 6 +- 10 files changed, 117 insertions(+), 45 deletions(-) diff --git a/compiler/cli/src/org/jetbrains/jet/cli/CompileSession.java b/compiler/cli/src/org/jetbrains/jet/cli/CompileSession.java index 90807c39b91..120a431fe08 100644 --- a/compiler/cli/src/org/jetbrains/jet/cli/CompileSession.java +++ b/compiler/cli/src/org/jetbrains/jet/cli/CompileSession.java @@ -1,5 +1,6 @@ package org.jetbrains.jet.cli; +import com.google.common.base.Predicates; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; @@ -82,7 +83,7 @@ public class CompileSession { final AnalyzingUtils instance = AnalyzingUtils.getInstance(JavaDefaultImports.JAVA_DEFAULT_IMPORTS); List allNamespaces = new ArrayList(mySourceFileNamespaces); allNamespaces.addAll(myLibrarySourceFileNamespaces); - myBindingContext = instance.analyzeNamespaces(myEnvironment.getProject(), allNamespaces, JetControlFlowDataTraceFactory.EMPTY); + myBindingContext = instance.analyzeNamespaces(myEnvironment.getProject(), allNamespaces, Predicates.alwaysTrue(), JetControlFlowDataTraceFactory.EMPTY); ErrorCollector errorCollector = new ErrorCollector(myBindingContext); errorCollector.report(); return !errorCollector.hasErrors; diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/AnalyzerFacade.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/AnalyzerFacade.java index b14585f1d4d..826e741d31a 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/AnalyzerFacade.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/AnalyzerFacade.java @@ -1,7 +1,9 @@ package org.jetbrains.jet.lang.resolve.java; +import com.google.common.base.Predicates; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.util.Key; +import com.intellij.psi.PsiFile; import com.intellij.psi.util.CachedValue; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; @@ -58,6 +60,7 @@ public class AnalyzerFacade { BindingContext bindingContext = analyzingUtils.analyzeNamespaces( file.getProject(), declarationProvider.fun(file), + Predicates.equalTo(file), JetControlFlowDataTraceFactory.EMPTY); return new Result(bindingContext, PsiModificationTracker.MODIFICATION_COUNT); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnalyzingUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnalyzingUtils.java index 70e6a3be7f9..753b8709857 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnalyzingUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnalyzingUtils.java @@ -1,9 +1,12 @@ package org.jetbrains.jet.lang.resolve; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import com.intellij.psi.PsiErrorElement; +import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.JetSemanticServices; import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory; @@ -59,19 +62,24 @@ public class AnalyzingUtils { Project project = namespace.getProject(); List declarations = Collections.singletonList(namespace); - return analyzeNamespaces(project, declarations, flowDataTraceFactory); + return analyzeNamespaces(project, declarations, Predicates.equalTo(namespace.getContainingFile()), flowDataTraceFactory); } - public BindingContext analyzeNamespaces(@NotNull Project project, @NotNull Collection declarations, @NotNull JetControlFlowDataTraceFactory flowDataTraceFactory) { + public BindingContext analyzeNamespaces( + @NotNull Project project, + @NotNull Collection declarations, + @NotNull Predicate filesToAnalyzeCompletely, + @NotNull JetControlFlowDataTraceFactory flowDataTraceFactory) { BindingTraceContext bindingTraceContext = new BindingTraceContext(); JetSemanticServices semanticServices = JetSemanticServices.createSemanticServices(project); JetScope libraryScope = semanticServices.getStandardLibrary().getLibraryScope(); ModuleDescriptor owner = new ModuleDescriptor(""); -// final WritableScope scope = new WritableScopeImpl(libraryScope, owner, new TraceBasedRedeclarationHandler(bindingTraceContext)).setDebugName("Root scope in analyzeNamespace"); + final WritableScope scope = new WritableScopeImpl(JetScope.EMPTY, owner, new TraceBasedRedeclarationHandler(bindingTraceContext)).setDebugName("Root scope in analyzeNamespace"); importingStrategy.addImports(project, semanticServices, bindingTraceContext, scope); scope.importScope(libraryScope); + TopDownAnalyzer.process(semanticServices, bindingTraceContext, scope, new NamespaceLike.Adapter(owner) { @Override @@ -103,7 +111,7 @@ public class AnalyzingUtils { public ClassObjectStatus setClassObjectDescriptor(@NotNull MutableClassDescriptor classObjectDescriptor) { throw new IllegalStateException("Must be guaranteed not to happen by the parser"); } - }, declarations, flowDataTraceFactory); + }, declarations, filesToAnalyzeCompletely, flowDataTraceFactory); return bindingTraceContext.getBindingContext(); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java index 7bc2afd02c8..b0c86e3a500 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java @@ -71,7 +71,6 @@ public class BodyResolver { } - public void resolveBehaviorDeclarationBodies() { resolveDelegationSpecifierLists(); @@ -86,34 +85,6 @@ public class BodyResolver { computeDeferredTypes(); } - private void computeDeferredTypes() { - Collection deferredTypes = context.getTrace().get(DEFERRED_TYPES, DEFERRED_TYPE_KEY); - if (deferredTypes != null) { - final Queue queue = new Queue(deferredTypes.size()); - context.getTrace().addHandler(DEFERRED_TYPE, new ObservableBindingTrace.RecordHandler() { - @Override - public void handleRecord(WritableSlice deferredTypeKeyDeferredTypeWritableSlice, BindingContext.DeferredTypeKey key, DeferredType value) { - queue.addLast(value); - } - }); - for (DeferredType deferredType : deferredTypes) { - queue.addLast(deferredType); - } - while (!queue.isEmpty()) { - DeferredType deferredType = queue.pullFirst(); - if (!deferredType.isComputed()) { - try { - deferredType.getActualType(); // to compute - } - catch (ReenteringLazyValueComputationException e) { - // A problem should be reported while computing the type - } - } - } - } - } - - private void resolveDelegationSpecifierLists() { // TODO : Make sure the same thing is not initialized twice for (Map.Entry entry : context.getClasses().entrySet()) { @@ -125,6 +96,7 @@ public class BodyResolver { } private void resolveDelegationSpecifierList(final JetClassOrObject jetClass, final MutableClassDescriptor descriptor) { + if (!context.completeAnalysisNeeded(jetClass)) return; final ConstructorDescriptor primaryConstructor = descriptor.getUnsubstitutedPrimaryConstructor(); final JetScope scopeForConstructor = primaryConstructor == null ? null @@ -278,7 +250,6 @@ public class BodyResolver { } private void resolveClassAnnotations() { - } private void resolveAnonymousInitializers() { @@ -291,6 +262,7 @@ public class BodyResolver { } private void resolveAnonymousInitializers(JetClassOrObject jetClassOrObject, MutableClassDescriptor classDescriptor) { + if (!context.completeAnalysisNeeded(jetClassOrObject)) return; List anonymousInitializers = jetClassOrObject.getAnonymousInitializers(); if (jetClassOrObject.hasPrimaryConstructor()) { ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor(); @@ -320,6 +292,7 @@ public class BodyResolver { } private void resolveSecondaryConstructorBody(JetConstructor declaration, final ConstructorDescriptor descriptor, final JetScope declaringScope) { + if (!context.completeAnalysisNeeded(declaration)) return; final JetScope functionInnerScope = getInnerScopeForConstructor(descriptor, declaringScope, false); final CallResolver callResolver = new CallResolver(context.getSemanticServices(), DataFlowInfo.EMPTY); // TODO: dataFlowInfo @@ -419,6 +392,7 @@ public class BodyResolver { Set processed = Sets.newHashSet(); for (Map.Entry entry : context.getClasses().entrySet()) { JetClass jetClass = entry.getKey(); + if (!context.completeAnalysisNeeded(jetClass)) continue; MutableClassDescriptor classDescriptor = entry.getValue(); for (JetProperty property : jetClass.getProperties()) { @@ -444,6 +418,7 @@ public class BodyResolver { // Top-level properties & properties of objects for (Map.Entry entry : this.context.getProperties().entrySet()) { JetProperty property = entry.getKey(); + if (!context.completeAnalysisNeeded(property)) return; if (processed.contains(property)) continue; final PropertyDescriptor propertyDescriptor = entry.getValue(); @@ -556,6 +531,7 @@ public class BodyResolver { @NotNull JetDeclarationWithBody function, @NotNull FunctionDescriptor functionDescriptor, @NotNull JetScope declaringScope) { + if (!context.completeAnalysisNeeded(function)) return; JetExpression bodyExpression = function.getBodyExpression(); if (bodyExpression != null) { @@ -580,4 +556,31 @@ public class BodyResolver { assert functionDescriptor.getReturnType() != null; } + + private void computeDeferredTypes() { + Collection deferredTypes = context.getTrace().get(DEFERRED_TYPES, DEFERRED_TYPE_KEY); + if (deferredTypes != null) { + final Queue queue = new Queue(deferredTypes.size()); + context.getTrace().addHandler(DEFERRED_TYPE, new ObservableBindingTrace.RecordHandler() { + @Override + public void handleRecord(WritableSlice deferredTypeKeyDeferredTypeWritableSlice, BindingContext.DeferredTypeKey key, DeferredType value) { + queue.addLast(value); + } + }); + for (DeferredType deferredType : deferredTypes) { + queue.addLast(deferredType); + } + while (!queue.isEmpty()) { + DeferredType deferredType = queue.pullFirst(); + if (!deferredType.isComputed()) { + try { + deferredType.getActualType(); // to compute + } + catch (ReenteringLazyValueComputationException e) { + // A problem should be reported while computing the type + } + } + } + } + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ControlFlowAnalyzer.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ControlFlowAnalyzer.java index fbea9ad37d4..2efa0842a4b 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ControlFlowAnalyzer.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ControlFlowAnalyzer.java @@ -42,6 +42,8 @@ public class ControlFlowAnalyzer { JetNamedFunction function = entry.getKey(); FunctionDescriptorImpl functionDescriptor = entry.getValue(); + if (!context.completeAnalysisNeeded(function)) continue; + final JetType expectedReturnType = !function.hasBlockBody() && !function.hasDeclaredReturnType() ? NO_EXPECTED_TYPE : functionDescriptor.getReturnType(); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DeclarationsChecker.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DeclarationsChecker.java index acebd21b8e6..8a7beb5a7f8 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DeclarationsChecker.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DeclarationsChecker.java @@ -40,7 +40,8 @@ public class DeclarationsChecker { for (Map.Entry entry : classes.entrySet()) { JetClass aClass = entry.getKey(); MutableClassDescriptor classDescriptor = entry.getValue(); - + if (!context.completeAnalysisNeeded(aClass)) continue; + checkClass(aClass, classDescriptor); checkModifiers(aClass.getModifierList()); } @@ -50,6 +51,7 @@ public class DeclarationsChecker { JetObjectDeclaration objectDeclaration = entry.getKey(); MutableClassDescriptor objectDescriptor = entry.getValue(); + if (!context.completeAnalysisNeeded(objectDeclaration)) continue; checkObject(objectDeclaration, objectDescriptor); } @@ -58,6 +60,7 @@ public class DeclarationsChecker { JetNamedFunction function = entry.getKey(); FunctionDescriptorImpl functionDescriptor = entry.getValue(); + if (!context.completeAnalysisNeeded(function)) continue; checkFunction(function, functionDescriptor); checkModifiers(function.getModifierList()); } @@ -67,6 +70,7 @@ public class DeclarationsChecker { JetProperty property = entry.getKey(); PropertyDescriptor propertyDescriptor = entry.getValue(); + if (!context.completeAnalysisNeeded(property)) continue; checkProperty(property, propertyDescriptor); checkModifiers(property.getModifierList()); } @@ -77,6 +81,7 @@ public class DeclarationsChecker { for (Map.Entry entry : context.getClasses().entrySet()) { MutableClassDescriptor classDescriptor = entry.getValue(); JetClass jetClass = entry.getKey(); + if (!context.completeAnalysisNeeded(jetClass)) return; if (classDescriptor.getUnsubstitutedPrimaryConstructor() == null && !(classDescriptor.getKind() == ClassKind.TRAIT)) { for (PropertyDescriptor propertyDescriptor : classDescriptor.getProperties()) { if (context.getTrace().getBindingContext().get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor)) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TopDownAnalysisContext.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TopDownAnalysisContext.java index 2acc11bc2c9..1fd0bad84f5 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TopDownAnalysisContext.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TopDownAnalysisContext.java @@ -1,13 +1,18 @@ package org.jetbrains.jet.lang.resolve; +import com.google.common.base.Predicate; import com.google.common.collect.Maps; import com.google.common.collect.Sets; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.JetSemanticServices; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.resolve.scopes.WritableScope; +import java.io.PrintStream; import java.util.Map; import java.util.Set; @@ -32,10 +37,42 @@ import java.util.Set; private final Map properties = Maps.newLinkedHashMap(); private final Set primaryConstructorParameterProperties = Sets.newHashSet(); - public TopDownAnalysisContext(JetSemanticServices semanticServices, BindingTrace trace) { + private final Predicate analyzeCompletely; + + private StringBuilder debugOutput; + + public TopDownAnalysisContext(JetSemanticServices semanticServices, BindingTrace trace, Predicate analyzeCompletely) { this.trace = new ObservableBindingTrace(trace); this.semanticServices = semanticServices; this.classDescriptorResolver = semanticServices.getClassDescriptorResolver(trace); + this.analyzeCompletely = analyzeCompletely; + } + + public void debug(Object message) { + if (debugOutput != null) { + debugOutput.append(message).append("\n"); + } + } + + /*package*/ void enableDebugOutput() { + if (debugOutput == null) { + debugOutput = new StringBuilder(); + } + } + + /*package*/ void printDebugOutput(PrintStream out) { + if (debugOutput != null) { + out.print(debugOutput); + } + } + + public boolean completeAnalysisNeeded(@NotNull PsiElement element) { + PsiFile containingFile = element.getContainingFile(); + boolean result = containingFile != null && analyzeCompletely.apply(containingFile); + if (!result) { + debug(containingFile); + } + return result; } public ObservableBindingTrace getTrace() { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java index e0cf26bce1b..f4cc0d05f89 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java @@ -1,5 +1,8 @@ package org.jetbrains.jet.lang.resolve; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.JetSemanticServices; import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory; @@ -25,21 +28,25 @@ public class TopDownAnalyzer { @NotNull JetSemanticServices semanticServices, @NotNull BindingTrace trace, @NotNull JetScope outerScope, - NamespaceLike owner, + @NotNull NamespaceLike owner, @NotNull Collection declarations, + @NotNull Predicate analyzeCompletely, @NotNull JetControlFlowDataTraceFactory flowDataTraceFactory) { - process(semanticServices, trace, outerScope, owner, declarations, flowDataTraceFactory, false); + process(semanticServices, trace, outerScope, owner, declarations, analyzeCompletely, flowDataTraceFactory, false); } private static void process( @NotNull JetSemanticServices semanticServices, @NotNull BindingTrace trace, @NotNull JetScope outerScope, - NamespaceLike owner, + @NotNull NamespaceLike owner, @NotNull Collection declarations, + @NotNull Predicate analyzeCompletely, @NotNull JetControlFlowDataTraceFactory flowDataTraceFactory, boolean declaredLocally) { - TopDownAnalysisContext context = new TopDownAnalysisContext(semanticServices, trace); + TopDownAnalysisContext context = new TopDownAnalysisContext(semanticServices, trace, analyzeCompletely); +// context.enableDebugOutput(); + new TypeHierarchyResolver(context).process(outerScope, owner, declarations); new DeclarationResolver(context).process(); new DelegationResolver(context).process(); @@ -47,13 +54,15 @@ public class TopDownAnalyzer { new BodyResolver(context).resolveBehaviorDeclarationBodies(); new DeclarationsChecker(context).process(); new ControlFlowAnalyzer(context, flowDataTraceFactory, declaredLocally).process(); + + context.printDebugOutput(System.out); } public static void processStandardLibraryNamespace( @NotNull JetSemanticServices semanticServices, @NotNull BindingTrace trace, @NotNull WritableScope outerScope, @NotNull NamespaceDescriptorImpl standardLibraryNamespace, @NotNull JetNamespace namespace) { - TopDownAnalysisContext context = new TopDownAnalysisContext(semanticServices, trace); + TopDownAnalysisContext context = new TopDownAnalysisContext(semanticServices, trace, Predicates.alwaysTrue()); context.getNamespaceScopes().put(namespace, standardLibraryNamespace.getMemberScope()); context.getNamespaceDescriptors().put(namespace, standardLibraryNamespace); context.getDeclaringScopes().put(namespace, outerScope); @@ -107,7 +116,7 @@ public class TopDownAnalyzer { public ClassObjectStatus setClassObjectDescriptor(@NotNull MutableClassDescriptor classObjectDescriptor) { return ClassObjectStatus.NOT_ALLOWED; } - }, Collections.singletonList(object), JetControlFlowDataTraceFactory.EMPTY, true); + }, Collections.singletonList(object), Predicates.equalTo(object.getContainingFile()), JetControlFlowDataTraceFactory.EMPTY, true); } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TypeHierarchyResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TypeHierarchyResolver.java index 0b7dc1377a1..147a2eddee6 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TypeHierarchyResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TypeHierarchyResolver.java @@ -36,7 +36,7 @@ public class TypeHierarchyResolver { this.context = context; } - public void process(@NotNull JetScope outerScope, NamespaceLike owner, @NotNull Collection declarations) { + public void process(@NotNull JetScope outerScope, @NotNull NamespaceLike owner, @NotNull Collection declarations) { collectNamespacesAndClassifiers(outerScope, owner, declarations); // namespaceScopes, classes processTypeImports(); diff --git a/idea/src/org/jetbrains/jet/plugin/compiler/JetCompiler.java b/idea/src/org/jetbrains/jet/plugin/compiler/JetCompiler.java index 0b6854b3770..7327491ec3c 100644 --- a/idea/src/org/jetbrains/jet/plugin/compiler/JetCompiler.java +++ b/idea/src/org/jetbrains/jet/plugin/compiler/JetCompiler.java @@ -1,5 +1,6 @@ package org.jetbrains.jet.plugin.compiler; +import com.google.common.base.Predicates; import com.google.common.collect.Lists; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.compiler.CompileContext; @@ -77,7 +78,10 @@ public class JetCompiler implements TranslatingCompiler { } } - BindingContext bindingContext = AnalyzingUtils.getInstance(JavaDefaultImports.JAVA_DEFAULT_IMPORTS).analyzeNamespaces(compileContext.getProject(), namespaces, JetControlFlowDataTraceFactory.EMPTY); + BindingContext bindingContext = AnalyzingUtils.getInstance(JavaDefaultImports.JAVA_DEFAULT_IMPORTS).analyzeNamespaces( + compileContext.getProject(), namespaces, + Predicates.alwaysTrue(), + JetControlFlowDataTraceFactory.EMPTY); boolean errors = false; for (Diagnostic diagnostic : bindingContext.getDiagnostics()) {