From 891dd9fc1c4dc1a6b06e7bffe77bfc22046d7818 Mon Sep 17 00:00:00 2001 From: "Pavel V. Talanov" Date: Tue, 24 Apr 2012 20:31:23 +0400 Subject: [PATCH] extract compiler error reporting functionality from KotlinToJVMBytecodeCompiler --- .../compiler/KotlinToJVMBytecodeCompiler.java | 133 ++++----------- .../messages/AnalyzerWithCompilerReport.java | 154 ++++++++++++++++++ 2 files changed, 182 insertions(+), 105 deletions(-) create mode 100644 compiler/cli/src/org/jetbrains/jet/compiler/messages/AnalyzerWithCompilerReport.java diff --git a/compiler/cli/src/org/jetbrains/jet/compiler/KotlinToJVMBytecodeCompiler.java b/compiler/cli/src/org/jetbrains/jet/compiler/KotlinToJVMBytecodeCompiler.java index da6fbd71787..13945fd5949 100644 --- a/compiler/cli/src/org/jetbrains/jet/compiler/KotlinToJVMBytecodeCompiler.java +++ b/compiler/cli/src/org/jetbrains/jet/compiler/KotlinToJVMBytecodeCompiler.java @@ -19,12 +19,7 @@ package org.jetbrains.jet.compiler; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Ref; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiErrorElement; import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiRecursiveElementWalkingVisitor; import com.intellij.testFramework.LightVirtualFile; import com.intellij.util.LocalTimeCounter; import jet.modules.Module; @@ -32,17 +27,12 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.analyzer.AnalyzeExhaust; import org.jetbrains.jet.codegen.*; +import org.jetbrains.jet.compiler.messages.AnalyzerWithCompilerReport; import org.jetbrains.jet.compiler.messages.CompilerMessageLocation; import org.jetbrains.jet.compiler.messages.CompilerMessageSeverity; -import org.jetbrains.jet.compiler.messages.MessageCollector; import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory; -import org.jetbrains.jet.lang.descriptors.ClassDescriptor; -import org.jetbrains.jet.lang.diagnostics.*; -import org.jetbrains.jet.lang.diagnostics.rendering.DefaultErrorMessages; import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.psi.JetPsiUtil; -import org.jetbrains.jet.lang.resolve.BindingContext; -import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.resolve.FqName; import org.jetbrains.jet.lang.resolve.java.AnalyzerFacadeForJVM; import org.jetbrains.jet.lang.resolve.java.JvmAbi; @@ -53,7 +43,6 @@ import org.jetbrains.jet.utils.Progress; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; -import java.util.Collection; import java.util.List; /** @@ -61,7 +50,9 @@ import java.util.List; * @author abreslav */ public class KotlinToJVMBytecodeCompiler { - private static final SimpleDiagnosticFactory SYNTAX_ERROR_FACTORY = SimpleDiagnosticFactory.create(Severity.ERROR); + + private KotlinToJVMBytecodeCompiler() { + } @Nullable public static ClassFileFactory compileModule( @@ -111,7 +102,8 @@ public class KotlinToJVMBytecodeCompiler { for (Module moduleBuilder : modules) { // TODO: this should be done only once for the environment if (configuration.getCompilerDependencies().getRuntimeJar() != null) { - CompileEnvironmentUtil.addToClasspath(configuration.getEnvironment(), configuration.getCompilerDependencies().getRuntimeJar()); + CompileEnvironmentUtil + .addToClasspath(configuration.getEnvironment(), configuration.getCompilerDependencies().getRuntimeJar()); } ClassFileFactory moduleFactory = KotlinToJVMBytecodeCompiler.compileModule(configuration, moduleBuilder, directory); if (moduleFactory == null) { @@ -135,7 +127,6 @@ public class KotlinToJVMBytecodeCompiler { private static boolean compileBunchOfSources( CompileEnvironmentConfiguration configuration, - String jar, String outputDir, boolean includeRuntime @@ -161,7 +152,8 @@ public class KotlinToJVMBytecodeCompiler { if (jar != null) { try { CompileEnvironmentUtil.writeToJar(factory, new FileOutputStream(jar), mainClass, includeRuntime); - } catch (FileNotFoundException e) { + } + catch (FileNotFoundException e) { throw new CompileEnvironmentException("Invalid jar path " + jar, e); } } @@ -203,7 +195,8 @@ public class KotlinToJVMBytecodeCompiler { CompileEnvironmentConfiguration configuration, String code) { - configuration.getEnvironment().addSources(new LightVirtualFile("script" + LocalTimeCounter.currentTime() + ".kt", JetLanguage.INSTANCE, code)); + configuration.getEnvironment() + .addSources(new LightVirtualFile("script" + LocalTimeCounter.currentTime() + ".kt", JetLanguage.INSTANCE, code)); GenerationState generationState = analyzeAndGenerate(configuration); if (generationState == null) { @@ -237,51 +230,27 @@ public class KotlinToJVMBytecodeCompiler { @Nullable private static AnalyzeExhaust analyze( final CompileEnvironmentConfiguration configuration, - boolean stubs) { - final Ref hasErrors = new Ref(false); - final MessageCollector messageCollectorWrapper = new MessageCollector() { - @Override - public void report(@NotNull CompilerMessageSeverity severity, - @NotNull String message, - @NotNull CompilerMessageLocation location) { - if (CompilerMessageSeverity.ERRORS.contains(severity)) { - hasErrors.set(true); - } - configuration.getMessageCollector().report(severity, message, location); - } - }; - JetCoreEnvironment environment = configuration.getEnvironment(); - - // Report syntax errors - for (JetFile file : environment.getSourceFiles()) { - file.accept(new PsiRecursiveElementWalkingVisitor() { - @Override - public void visitErrorElement(PsiErrorElement element) { - String description = element.getErrorDescription(); - String message = StringUtil.isEmpty(description) ? "Syntax error" : description; - Diagnostic diagnostic = new SyntaxErrorDiagnostic(element, Severity.ERROR, message); - reportDiagnostic(messageCollectorWrapper, diagnostic); - } - }); - } - - // Analyze and report semantic errors - Predicate filesToAnalyzeCompletely = + final JetCoreEnvironment environment = configuration.getEnvironment(); + AnalyzerWithCompilerReport analyzerWithCompilerReport = new AnalyzerWithCompilerReport(configuration.getMessageCollector()); + final Predicate filesToAnalyzeCompletely = stubs ? Predicates.alwaysFalse() : Predicates.alwaysTrue(); - AnalyzeExhaust exhaust = AnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration( - environment.getProject(), environment.getSourceFiles(), filesToAnalyzeCompletely, JetControlFlowDataTraceFactory.EMPTY, - configuration.getCompilerDependencies()); + analyzerWithCompilerReport.analyzeAndReport( + new AnalyzerWithCompilerReport.AnalyzerWrapper() { + @NotNull + @Override + public AnalyzeExhaust analyze() { + return AnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration( + environment.getProject(), environment.getSourceFiles(), filesToAnalyzeCompletely, + JetControlFlowDataTraceFactory.EMPTY, + configuration.getCompilerDependencies()); + } + }, environment.getSourceFiles() + ); - for (Diagnostic diagnostic : exhaust.getBindingContext().getDiagnostics()) { - reportDiagnostic(messageCollectorWrapper, diagnostic); - } - - reportIncompleteHierarchies(messageCollectorWrapper, exhaust); - - return hasErrors.get() ? null : exhaust; + return analyzerWithCompilerReport.hasErrors() ? null : analyzerWithCompilerReport.getAnalyzeExhaust(); } @NotNull @@ -300,7 +269,8 @@ public class KotlinToJVMBytecodeCompiler { } }; GenerationState generationState = new GenerationState(project, ClassBuilderFactories.binaries(stubs), backendProgress, - exhaust, environment.getSourceFiles(), configuration.getCompilerDependencies().getCompilerSpecialMode()); + exhaust, environment.getSourceFiles(), + configuration.getCompilerDependencies().getCompilerSpecialMode()); generationState.compileCorrectFiles(CompilationErrorHandler.THROW_EXCEPTION); List plugins = configuration.getCompilerPlugins(); @@ -312,51 +282,4 @@ public class KotlinToJVMBytecodeCompiler { } return generationState; } - - private static void reportDiagnostic(MessageCollector collector, Diagnostic diagnostic) { - DiagnosticUtils.LineAndColumn lineAndColumn = DiagnosticUtils.getLineAndColumn(diagnostic); - VirtualFile virtualFile = diagnostic.getPsiFile().getVirtualFile(); - String path = virtualFile == null ? null : virtualFile.getPath(); - String render; - if (diagnostic.getFactory() == SYNTAX_ERROR_FACTORY) { - render = ((SyntaxErrorDiagnostic)diagnostic).message; - } - else { - render = DefaultErrorMessages.RENDERER.render(diagnostic); - } - collector.report(convertSeverity(diagnostic.getSeverity()), render, CompilerMessageLocation.create(path, lineAndColumn.getLine(), lineAndColumn.getColumn())); - } - - private static CompilerMessageSeverity convertSeverity(Severity severity) { - switch (severity) { - case INFO: - return CompilerMessageSeverity.INFO; - case ERROR: - return CompilerMessageSeverity.ERROR; - case WARNING: - return CompilerMessageSeverity.WARNING; - } - throw new IllegalStateException("Unknown severity: " + severity); - } - - private static void reportIncompleteHierarchies(MessageCollector collector, AnalyzeExhaust exhaust) { - Collection incompletes = exhaust.getBindingContext().getKeys(BindingContext.INCOMPLETE_HIERARCHY); - if (!incompletes.isEmpty()) { - StringBuilder message = new StringBuilder("The following classes have incomplete hierarchies:\n"); - for (ClassDescriptor incomplete : incompletes) { - String fqName = DescriptorUtils.getFQName(incomplete).getFqName(); - message.append(" ").append(fqName).append("\n"); - } - collector.report(CompilerMessageSeverity.ERROR, message.toString(), CompilerMessageLocation.NO_LOCATION); - } - } - - private static class SyntaxErrorDiagnostic extends SimpleDiagnostic { - private String message; - - private SyntaxErrorDiagnostic(@NotNull PsiErrorElement psiElement, @NotNull Severity severity, String message) { - super(psiElement, SYNTAX_ERROR_FACTORY, severity); - this.message = message; - } - } } diff --git a/compiler/cli/src/org/jetbrains/jet/compiler/messages/AnalyzerWithCompilerReport.java b/compiler/cli/src/org/jetbrains/jet/compiler/messages/AnalyzerWithCompilerReport.java new file mode 100644 index 00000000000..d03a19efd8e --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/compiler/messages/AnalyzerWithCompilerReport.java @@ -0,0 +1,154 @@ +/* + * Copyright 2010-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. + */ + +package org.jetbrains.jet.compiler.messages; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiErrorElement; +import com.intellij.psi.PsiRecursiveElementWalkingVisitor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.analyzer.AnalyzeExhaust; +import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.diagnostics.*; +import org.jetbrains.jet.lang.diagnostics.rendering.DefaultErrorMessages; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.DescriptorUtils; + +import java.util.Collection; + +/** + * @author Pavel Talanov + */ +public final class AnalyzerWithCompilerReport { + + @NotNull + private static CompilerMessageSeverity convertSeverity(@NotNull Severity severity) { + switch (severity) { + case INFO: + return CompilerMessageSeverity.INFO; + case ERROR: + return CompilerMessageSeverity.ERROR; + case WARNING: + return CompilerMessageSeverity.WARNING; + } + throw new IllegalStateException("Unknown severity: " + severity); + } + + @NotNull + private static final SimpleDiagnosticFactory SYNTAX_ERROR_FACTORY = SimpleDiagnosticFactory.create(Severity.ERROR); + + private boolean hasErrors; + @NotNull + private final MessageCollector messageCollectorWrapper; + @Nullable + private AnalyzeExhaust analyzeExhaust = null; + + public AnalyzerWithCompilerReport(@NotNull final MessageCollector collector) { + messageCollectorWrapper = new MessageCollector() { + @Override + public void report(@NotNull CompilerMessageSeverity severity, + @NotNull String message, + @NotNull CompilerMessageLocation location) { + if (CompilerMessageSeverity.ERRORS.contains(severity)) { + hasErrors = true; + } + collector.report(severity, message, location); + } + }; + } + + private void reportDiagnostic(@NotNull Diagnostic diagnostic) { + DiagnosticUtils.LineAndColumn lineAndColumn = DiagnosticUtils.getLineAndColumn(diagnostic); + VirtualFile virtualFile = diagnostic.getPsiFile().getVirtualFile(); + String path = virtualFile == null ? null : virtualFile.getPath(); + String render; + if (diagnostic.getFactory() == SYNTAX_ERROR_FACTORY) { + render = ((SyntaxErrorDiagnostic)diagnostic).message; + } + else { + render = DefaultErrorMessages.RENDERER.render(diagnostic); + } + messageCollectorWrapper.report(convertSeverity(diagnostic.getSeverity()), render, + CompilerMessageLocation.create(path, lineAndColumn.getLine(), lineAndColumn.getColumn())); + } + + private void reportIncompleteHierarchies() { + assert analyzeExhaust != null; + Collection incompletes = analyzeExhaust.getBindingContext().getKeys(BindingContext.INCOMPLETE_HIERARCHY); + if (!incompletes.isEmpty()) { + StringBuilder message = new StringBuilder("The following classes have incomplete hierarchies:\n"); + for (ClassDescriptor incomplete : incompletes) { + String fqName = DescriptorUtils.getFQName(incomplete).getFqName(); + message.append(" ").append(fqName).append("\n"); + } + messageCollectorWrapper.report(CompilerMessageSeverity.ERROR, message.toString(), CompilerMessageLocation.NO_LOCATION); + } + } + + private void reportDiagnostics() { + assert analyzeExhaust != null; + for (Diagnostic diagnostic : analyzeExhaust.getBindingContext().getDiagnostics()) { + reportDiagnostic(diagnostic); + } + } + + private void reportSyntaxErrors(@NotNull Collection files) { + for (JetFile file : files) { + file.accept(new PsiRecursiveElementWalkingVisitor() { + @Override + public void visitErrorElement(PsiErrorElement element) { + String description = element.getErrorDescription(); + String message = StringUtil.isEmpty(description) ? "Syntax error" : description; + Diagnostic diagnostic = new SyntaxErrorDiagnostic(element, Severity.ERROR, message); + reportDiagnostic(diagnostic); + } + }); + } + } + + @Nullable + public AnalyzeExhaust getAnalyzeExhaust() { + return analyzeExhaust; + } + + public boolean hasErrors() { + return hasErrors; + } + + public void analyzeAndReport(@NotNull AnalyzerWrapper analyzerWrapper, @NotNull Collection files) { + reportSyntaxErrors(files); + analyzeExhaust = analyzerWrapper.analyze(); + reportDiagnostics(); + reportIncompleteHierarchies(); + } + + public interface AnalyzerWrapper { + @NotNull + AnalyzeExhaust analyze(); + } + + public static class SyntaxErrorDiagnostic extends SimpleDiagnostic { + private String message; + + public SyntaxErrorDiagnostic(@NotNull PsiErrorElement psiElement, @NotNull Severity severity, String message) { + super(psiElement, SYNTAX_ERROR_FACTORY, severity); + this.message = message; + } + } +}