diff --git a/compiler/cli/src/org/jetbrains/jet/cli/common/messages/AnalyzerWithCompilerReport.java b/compiler/cli/src/org/jetbrains/jet/cli/common/messages/AnalyzerWithCompilerReport.java index f5618b43992..d4534a55c8f 100644 --- a/compiler/cli/src/org/jetbrains/jet/cli/common/messages/AnalyzerWithCompilerReport.java +++ b/compiler/cli/src/org/jetbrains/jet/cli/common/messages/AnalyzerWithCompilerReport.java @@ -18,8 +18,8 @@ package org.jetbrains.jet.cli.common.messages; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiElement; import com.intellij.psi.PsiErrorElement; -import com.intellij.psi.PsiRecursiveElementWalkingVisitor; import jet.Function0; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -75,8 +75,8 @@ public final class AnalyzerWithCompilerReport { }; } - private void reportDiagnostic(@NotNull Diagnostic diagnostic) { - if (!diagnostic.isValid()) return; + private static boolean reportDiagnostic(@NotNull Diagnostic diagnostic, @NotNull MessageCollector messageCollector) { + if (!diagnostic.isValid()) return false; DiagnosticUtils.LineAndColumn lineAndColumn = DiagnosticUtils.getLineAndColumn(diagnostic); VirtualFile virtualFile = diagnostic.getPsiFile().getVirtualFile(); String path = virtualFile == null ? null : virtualFile.getPath(); @@ -87,8 +87,9 @@ public final class AnalyzerWithCompilerReport { else { render = DefaultErrorMessages.RENDERER.render(diagnostic); } - messageCollectorWrapper.report(convertSeverity(diagnostic.getSeverity()), render, - CompilerMessageLocation.create(path, lineAndColumn.getLine(), lineAndColumn.getColumn())); + messageCollector.report(convertSeverity(diagnostic.getSeverity()), render, + CompilerMessageLocation.create(path, lineAndColumn.getLine(), lineAndColumn.getColumn())); + return diagnostic.getSeverity() == Severity.ERROR; } private void reportIncompleteHierarchies() { @@ -104,27 +105,40 @@ public final class AnalyzerWithCompilerReport { } } - private void reportDiagnostics() { - assert analyzeExhaust != null; - for (Diagnostic diagnostic : analyzeExhaust.getBindingContext().getDiagnostics()) { - reportDiagnostic(diagnostic); + public static boolean reportDiagnostics(@NotNull BindingContext bindingContext, @NotNull MessageCollector messageCollector) { + boolean hasErrors = false; + for (Diagnostic diagnostic : bindingContext.getDiagnostics()) { + hasErrors |= reportDiagnostic(diagnostic, messageCollector); } + return hasErrors; } private void reportSyntaxErrors(@NotNull Collection files) { - for (JetFile file : files) { - file.accept(new AnalyzingUtils.PsiErrorElementVisitor() { - @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); - } - }); + for (PsiElement file : files) { + reportSyntaxErrors(file, AnalyzerWithCompilerReport.this.messageCollectorWrapper); } } + public static boolean reportSyntaxErrors(@NotNull PsiElement file, @NotNull final MessageCollector messageCollector) { + class ErrorReportingVisitor extends AnalyzingUtils.PsiErrorElementVisitor { + boolean hasErrors = false; + + @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, messageCollector); + hasErrors = true; + } + } + ErrorReportingVisitor visitor = new ErrorReportingVisitor(); + + file.accept(visitor); + + return visitor.hasErrors; + } + @Nullable public AnalyzeExhaust getAnalyzeExhaust() { return analyzeExhaust; @@ -137,7 +151,7 @@ public final class AnalyzerWithCompilerReport { public void analyzeAndReport(@NotNull Function0 analyzer, @NotNull Collection files) { reportSyntaxErrors(files); analyzeExhaust = analyzer.invoke(); - reportDiagnostics(); + reportDiagnostics(analyzeExhaust.getBindingContext(), messageCollectorWrapper); reportIncompleteHierarchies(); } diff --git a/compiler/cli/src/org/jetbrains/jet/cli/common/messages/MessageCollector.java b/compiler/cli/src/org/jetbrains/jet/cli/common/messages/MessageCollector.java index 44b2697262b..ab50ebec593 100644 --- a/compiler/cli/src/org/jetbrains/jet/cli/common/messages/MessageCollector.java +++ b/compiler/cli/src/org/jetbrains/jet/cli/common/messages/MessageCollector.java @@ -19,12 +19,7 @@ package org.jetbrains.jet.cli.common.messages; import org.jetbrains.annotations.NotNull; public interface MessageCollector { - MessageCollector PLAIN_TEXT_TO_SYSTEM_ERR = new MessageCollector() { - @Override - public void report(@NotNull CompilerMessageSeverity severity, @NotNull String message, @NotNull CompilerMessageLocation location) { - System.err.println(MessageRenderer.PLAIN.render(severity, message, location)); - } - }; + MessageCollector PLAIN_TEXT_TO_SYSTEM_ERR = new MessageCollectorPlainTextToStream(System.err); void report(@NotNull CompilerMessageSeverity severity, @NotNull String message, @NotNull CompilerMessageLocation location); } diff --git a/compiler/cli/src/org/jetbrains/jet/cli/common/messages/MessageCollectorPlainTextToStream.java b/compiler/cli/src/org/jetbrains/jet/cli/common/messages/MessageCollectorPlainTextToStream.java new file mode 100644 index 00000000000..6be25bbb609 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/common/messages/MessageCollectorPlainTextToStream.java @@ -0,0 +1,39 @@ +/* + * 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.cli.common.messages; + +import org.jetbrains.annotations.NotNull; + +import java.io.PrintStream; + +/** + * @author Stepan Koltsov + */ +public class MessageCollectorPlainTextToStream implements MessageCollector { + @NotNull + private final PrintStream stream; + + public MessageCollectorPlainTextToStream(@NotNull PrintStream stream) { + this.stream = stream; + } + + @Override + public void report(@NotNull CompilerMessageSeverity severity, @NotNull String message, @NotNull CompilerMessageLocation location) { + stream.println(MessageRenderer.PLAIN.render(severity, message, location)); + } + +} diff --git a/compiler/cli/src/org/jetbrains/jet/cli/common/messages/MessageCollectorToString.java b/compiler/cli/src/org/jetbrains/jet/cli/common/messages/MessageCollectorToString.java new file mode 100644 index 00000000000..c2e50476981 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/cli/common/messages/MessageCollectorToString.java @@ -0,0 +1,45 @@ +/* + * 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.cli.common.messages; + +import org.jetbrains.annotations.NotNull; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +/** + * @author Stepan Koltsov + */ +public class MessageCollectorToString implements MessageCollector { + private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + private final MessageCollector actualCollector = new MessageCollectorPlainTextToStream(new PrintStream(outputStream)); + + + @Override + public void report(@NotNull CompilerMessageSeverity severity, @NotNull String message, @NotNull CompilerMessageLocation location) { + actualCollector.report(severity, message, location); + } + + private static Charset UTF8 = Charset.forName("utf-8"); + + @NotNull + public String getString() { + return UTF8.decode(ByteBuffer.wrap(outputStream.toByteArray())).toString(); + } +} diff --git a/compiler/cli/src/org/jetbrains/jet/cli/jvm/repl/ReplFromTerminal.java b/compiler/cli/src/org/jetbrains/jet/cli/jvm/repl/ReplFromTerminal.java index 05c07940e94..09cb405cf7f 100644 --- a/compiler/cli/src/org/jetbrains/jet/cli/jvm/repl/ReplFromTerminal.java +++ b/compiler/cli/src/org/jetbrains/jet/cli/jvm/repl/ReplFromTerminal.java @@ -117,7 +117,10 @@ public class ReplFromTerminal { } ReplInterpreter.LineResult lineResult = getReplInterpreter().eval(line); - if (!lineResult.isUnit()) { + if (!lineResult.isSuccessful()) { + System.out.print(lineResult.getErrorText()); + } + else if (!lineResult.isUnit()) { System.out.println(lineResult.getValue()); } return true; diff --git a/compiler/cli/src/org/jetbrains/jet/cli/jvm/repl/ReplInterpreter.java b/compiler/cli/src/org/jetbrains/jet/cli/jvm/repl/ReplInterpreter.java index 419f715236a..fbf839f6e6f 100644 --- a/compiler/cli/src/org/jetbrains/jet/cli/jvm/repl/ReplInterpreter.java +++ b/compiler/cli/src/org/jetbrains/jet/cli/jvm/repl/ReplInterpreter.java @@ -17,6 +17,7 @@ package org.jetbrains.jet.cli.jvm.repl; import com.google.common.base.Predicates; +import com.google.common.base.Throwables; import com.google.common.collect.Lists; import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; @@ -29,6 +30,9 @@ import com.intellij.testFramework.LightVirtualFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.analyzer.AnalyzeExhaust; +import org.jetbrains.jet.cli.common.messages.AnalyzerWithCompilerReport; +import org.jetbrains.jet.cli.common.messages.MessageCollector; +import org.jetbrains.jet.cli.common.messages.MessageCollectorToString; import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment; import org.jetbrains.jet.codegen.ClassBuilderFactories; import org.jetbrains.jet.codegen.CompilationErrorHandler; @@ -40,7 +44,6 @@ import org.jetbrains.jet.lang.descriptors.NamespaceLikeBuilderDummy; import org.jetbrains.jet.lang.descriptors.ScriptDescriptor; import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.resolve.AnalyzerScriptParameter; -import org.jetbrains.jet.lang.resolve.AnalyzingUtils; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingTraceContext; import org.jetbrains.jet.lang.resolve.ScriptHeaderResolver; @@ -60,7 +63,6 @@ import org.jetbrains.jet.utils.ExceptionUtils; import org.jetbrains.jet.utils.Progress; import java.io.File; -import java.io.PrintStream; import java.io.PrintWriter; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -122,19 +124,52 @@ public class ReplInterpreter { public static class LineResult { private final Object value; private final boolean unit; + private final String errorText; - public LineResult(Object value, boolean unit) { + private LineResult(Object value, boolean unit, String errorText) { this.value = value; this.unit = unit; + this.errorText = errorText; + } + + public boolean isSuccessful() { + return errorText == null; + } + + private void checkSuccessful() { + if (!isSuccessful()) { + throw new IllegalStateException("it is error"); + } } public Object getValue() { + checkSuccessful(); return value; } public boolean isUnit() { + checkSuccessful(); return unit; } + + @NotNull + public String getErrorText() { + return errorText; + } + + public static LineResult successful(Object value, boolean unit) { + return new LineResult(value, unit, null); + } + + public static LineResult error(@NotNull String errorText) { + if (errorText.isEmpty()) { + errorText = ""; + } + else if (!errorText.endsWith("\n")) { + errorText = errorText + "\n"; + } + return new LineResult(null, false, errorText); + } } @NotNull @@ -147,13 +182,21 @@ public class ReplInterpreter { virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET); JetFile psiFile = (JetFile) ((PsiFileFactoryImpl) PsiFileFactory.getInstance(jetCoreEnvironment.getProject())).trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false); - AnalyzingUtils.checkForSyntacticErrors(psiFile); + MessageCollectorToString errorCollector = new MessageCollectorToString(); + + boolean hasErrors = AnalyzerWithCompilerReport.reportSyntaxErrors(psiFile, errorCollector); + if (hasErrors) { + return LineResult.error(errorCollector.getString()); + } injector.getTopDownAnalyzer().prepareForTheNextReplLine(); psiFile.getScript().putUserData(ScriptHeaderResolver.PRIORITY_KEY, lineNumber); - ScriptDescriptor scriptDescriptor = doAnalyze(psiFile); + ScriptDescriptor scriptDescriptor = doAnalyze(psiFile, errorCollector); + if (scriptDescriptor == null) { + return LineResult.error(errorCollector.getString()); + } Progress backendProgress = new Progress() { @Override @@ -188,14 +231,19 @@ public class ReplInterpreter { } Constructor scriptInstanceConstructor = scriptClass.getConstructor(constructorParams); - Object scriptInstance = scriptInstanceConstructor.newInstance(constructorArgs); + Object scriptInstance; + try { + scriptInstance = scriptInstanceConstructor.newInstance(constructorArgs); + } catch (Throwable e) { + return LineResult.error(Throwables.getStackTraceAsString(e)); + } Field rvField = scriptClass.getDeclaredField("rv"); rvField.setAccessible(true); Object rv = rvField.get(scriptInstance); earlierLines.add(new EarlierLine(line, scriptDescriptor, scriptClass, scriptInstance, scriptClassName)); - return new LineResult(rv, scriptDescriptor.getReturnType().equals(JetStandardClasses.getUnitType())); + return LineResult.successful(rv, scriptDescriptor.getReturnType().equals(JetStandardClasses.getUnitType())); } catch (Throwable e) { PrintWriter writer = new PrintWriter(System.err); classLoader.dumpClasses(writer); @@ -204,7 +252,8 @@ public class ReplInterpreter { } } - private ScriptDescriptor doAnalyze(@NotNull JetFile psiFile) { + @Nullable + private ScriptDescriptor doAnalyze(@NotNull JetFile psiFile, @NotNull MessageCollector messageCollector) { final WritableScope scope = new WritableScopeImpl( JetScope.EMPTY, module, new TraceBasedRedeclarationHandler(trace), "Root scope in analyzeNamespace"); @@ -231,8 +280,10 @@ public class ReplInterpreter { // namespaces added to module explicitly in injector.getTopDownAnalyzer().doProcess(scope, new NamespaceLikeBuilderDummy(), Collections.singletonList(psiFile)); - // TODO: print, not throw - AnalyzingUtils.throwExceptionOnErrors(trace.getBindingContext()); + boolean hasErrors = AnalyzerWithCompilerReport.reportDiagnostics(trace.getBindingContext(), messageCollector); + if (hasErrors) { + return null; + } ScriptDescriptor scriptDescriptor = injector.getTopDownAnalysisContext().getScripts().get(psiFile.getScript()); lastLineScope = trace.get(BindingContext.SCRIPT_SCOPE, scriptDescriptor); diff --git a/compiler/testData/repl/analyzeErrors.repl b/compiler/testData/repl/analyzeErrors.repl new file mode 100644 index 00000000000..4d569171215 --- /dev/null +++ b/compiler/testData/repl/analyzeErrors.repl @@ -0,0 +1,6 @@ +>>> fun foo() = 765 +null +>>> foo(1) +ERROR: /line2.ktscript: (1, 5) Too many arguments for internal final fun foo() : jet.Int defined in