extract compiler error reporting functionality from KotlinToJVMBytecodeCompiler
This commit is contained in:
@@ -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<PsiErrorElement> 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<Boolean> hasErrors = new Ref<Boolean>(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<PsiFile> filesToAnalyzeCompletely =
|
||||
final JetCoreEnvironment environment = configuration.getEnvironment();
|
||||
AnalyzerWithCompilerReport analyzerWithCompilerReport = new AnalyzerWithCompilerReport(configuration.getMessageCollector());
|
||||
final Predicate<PsiFile> filesToAnalyzeCompletely =
|
||||
stubs ? Predicates.<PsiFile>alwaysFalse() : Predicates.<PsiFile>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<CompilerPlugin> 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<ClassDescriptor> 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<PsiErrorElement> {
|
||||
private String message;
|
||||
|
||||
private SyntaxErrorDiagnostic(@NotNull PsiErrorElement psiElement, @NotNull Severity severity, String message) {
|
||||
super(psiElement, SYNTAX_ERROR_FACTORY, severity);
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<PsiErrorElement> 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<ClassDescriptor> 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<JetFile> 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<JetFile> files) {
|
||||
reportSyntaxErrors(files);
|
||||
analyzeExhaust = analyzerWrapper.analyze();
|
||||
reportDiagnostics();
|
||||
reportIncompleteHierarchies();
|
||||
}
|
||||
|
||||
public interface AnalyzerWrapper {
|
||||
@NotNull
|
||||
AnalyzeExhaust analyze();
|
||||
}
|
||||
|
||||
public static class SyntaxErrorDiagnostic extends SimpleDiagnostic<PsiErrorElement> {
|
||||
private String message;
|
||||
|
||||
public SyntaxErrorDiagnostic(@NotNull PsiErrorElement psiElement, @NotNull Severity severity, String message) {
|
||||
super(psiElement, SYNTAX_ERROR_FACTORY, severity);
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user