diff --git a/.idea/libraries/asm.xml b/.idea/libraries/asm.xml index afc4bcc3b627848973842068e51ff0acaffaede2..d343b26b41660e932816875477d5b845f3e496c1 100644 --- a/.idea/libraries/asm.xml +++ b/.idea/libraries/asm.xml @@ -1,7 +1,7 @@ - + diff --git a/compiler/cli/src/org/jetbrains/jet/cli/KotlinCompiler.java b/compiler/cli/src/org/jetbrains/jet/cli/KotlinCompiler.java index 62d0e459c6b96f7d1f827acb46cbbcab53ed069f..9738e1958e2e89740c2a6dd5ae69c802a7059ab2 100644 --- a/compiler/cli/src/org/jetbrains/jet/cli/KotlinCompiler.java +++ b/compiler/cli/src/org/jetbrains/jet/cli/KotlinCompiler.java @@ -19,12 +19,8 @@ package org.jetbrains.jet.cli; import com.sampullara.cli.Args; import com.sampullara.cli.Argument; import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.compiler.CompileEnvironment; -import org.jetbrains.jet.compiler.CompileEnvironmentException; -import org.jetbrains.jet.compiler.CompilerPlugin; -import org.jetbrains.jet.compiler.FileNameTransformer; +import org.jetbrains.jet.compiler.*; -import java.io.ByteArrayOutputStream; import java.io.PrintStream; /** @@ -72,6 +68,9 @@ public class KotlinCompiler { @Argument(value = "stubs", description = "Compile stubs: ignore function bodies") public boolean stubs; + @Argument(value = "tags", description = "Demarcate each compilation message (error, warning, etc) with an open and close tag") + public boolean tags; + @Argument(value = "transformNamesToJava", description = "Transform Kotlin file names to *.java. This option is needed for compiling kotlinized Java library headers") public boolean transformNamesToJava; } @@ -108,7 +107,8 @@ public class KotlinCompiler { return 1; } catch (Throwable t) { - t.printStackTrace(); + // Always use tags + errStream.println(MessageRenderer.TAGS.renderException(t)); return 1; } @@ -117,7 +117,11 @@ public class KotlinCompiler { return 0; } - CompileEnvironment environment = new CompileEnvironment(arguments.transformNamesToJava ? ANY_EXTENSION_TO_JAVA : FileNameTransformer.IDENTITY); + FileNameTransformer fileNameTransformer = arguments.transformNamesToJava + ? ANY_EXTENSION_TO_JAVA + : FileNameTransformer.IDENTITY; + MessageRenderer messageRenderer = arguments.tags ? MessageRenderer.TAGS : MessageRenderer.PLAIN; + CompileEnvironment environment = new CompileEnvironment(fileNameTransformer, messageRenderer); try { environment.setIgnoreErrors(false); environment.setErrorStream(errStream); @@ -138,16 +142,18 @@ public class KotlinCompiler { if (arguments.module != null) { environment.compileModuleScript(arguments.module, arguments.jar, arguments.outputDir, arguments.includeRuntime); - return 0; } else { - if (!environment.compileBunchOfSources(arguments.src, arguments.jar, arguments.outputDir, arguments.includeRuntime)) { - return 1; - } + environment.compileBunchOfSources(arguments.src, arguments.jar, arguments.outputDir, arguments.includeRuntime); } return 0; - } finally { + } + catch (Throwable t) { + errStream.println(messageRenderer.renderException(t)); + return 1; + } + finally { environment.dispose(); } } diff --git a/compiler/cli/src/org/jetbrains/jet/compiler/CompileEnvironment.java b/compiler/cli/src/org/jetbrains/jet/compiler/CompileEnvironment.java index a1dd8251b1758eb73f2038c5e80f3e89586a1901..833bbf8a885e6b761184e018cd8ac083069b455b 100644 --- a/compiler/cli/src/org/jetbrains/jet/compiler/CompileEnvironment.java +++ b/compiler/cli/src/org/jetbrains/jet/compiler/CompileEnvironment.java @@ -55,7 +55,9 @@ import java.util.jar.*; public class CompileEnvironment { private JetCoreEnvironment myEnvironment; private final Disposable myRootDisposable; - private PrintStream myErrorStream = System.out; + private final MessageRenderer myMessageRenderer; + private PrintStream myErrorStream = System.err; + private final FileNameTransformer myFileNameTransformer; private URL myStdlib; @@ -63,10 +65,10 @@ public class CompileEnvironment { private boolean stubs = false; public CompileEnvironment() { - this(FileNameTransformer.IDENTITY); + this(FileNameTransformer.IDENTITY, MessageRenderer.PLAIN); } - public CompileEnvironment(FileNameTransformer fileNameTransformer) { + public CompileEnvironment(FileNameTransformer fileNameTransformer, MessageRenderer messageRenderer) { myRootDisposable = new Disposable() { @Override public void dispose() { @@ -74,6 +76,7 @@ public class CompileEnvironment { }; myEnvironment = new JetCoreEnvironment(myRootDisposable); myFileNameTransformer = fileNameTransformer; + myMessageRenderer = messageRenderer; } public void setErrorStream(PrintStream errorStream) { @@ -145,7 +148,7 @@ public class CompileEnvironment { File rtJar = findRtJar(javaHome); if (rtJar == null || !rtJar.exists()) { - throw new CompileEnvironmentException("No JDK rt.jar found under" + javaHome); + throw new CompileEnvironmentException("No JDK rt.jar found under " + javaHome); } return rtJar; @@ -164,8 +167,8 @@ public class CompileEnvironment { return null; } - public void compileModuleScript(String moduleFile, @Nullable String jarPath, @Nullable String outputDir, boolean jarRuntime) { - final List modules = loadModuleScript(moduleFile); + public boolean compileModuleScript(String moduleFile, @Nullable String jarPath, @Nullable String outputDir, boolean jarRuntime) { + List modules = loadModuleScript(moduleFile); if (modules == null) { throw new CompileEnvironmentException("Module script " + moduleFile + " compilation failed"); @@ -178,20 +181,22 @@ public class CompileEnvironment { final String directory = new File(moduleFile).getParent(); for (Module moduleBuilder : modules) { ClassFileFactory moduleFactory = compileModule(moduleBuilder, directory); - if (moduleFactory != null) { - if (outputDir != null) { - writeToOutputDirectory(moduleFactory, outputDir); - } - else { - String path = jarPath != null ? jarPath : new File(directory, moduleBuilder.getModuleName() + ".jar").getPath(); - try { - writeToJar(moduleFactory, new FileOutputStream(path), null, jarRuntime); - } catch (FileNotFoundException e) { - throw new CompileEnvironmentException("Invalid jar path " + path, e); - } + if (moduleFactory == null) { + return false; + } + if (outputDir != null) { + writeToOutputDirectory(moduleFactory, outputDir); + } + else { + String path = jarPath != null ? jarPath : new File(directory, moduleBuilder.getModuleName() + ".jar").getPath(); + try { + writeToJar(moduleFactory, new FileOutputStream(path), null, jarRuntime); + } catch (FileNotFoundException e) { + throw new CompileEnvironmentException("Invalid jar path " + path, e); } } } + return true; } public List loadModuleScript(String moduleFile) { @@ -199,7 +204,7 @@ public class CompileEnvironment { scriptCompileSession.addSources(moduleFile); ensureRuntime(); - if (!scriptCompileSession.analyze(myErrorStream)) { + if (!scriptCompileSession.analyze(myErrorStream, myMessageRenderer)) { return null; } final ClassFileFactory factory = scriptCompileSession.generate(); @@ -258,7 +263,7 @@ public class CompileEnvironment { ensureRuntime(); - if (!moduleCompileSession.analyze(myErrorStream) && !ignoreErrors) { + if (!moduleCompileSession.analyze(myErrorStream, myMessageRenderer) && !ignoreErrors) { return null; } return moduleCompileSession.generate(); @@ -345,7 +350,7 @@ public class CompileEnvironment { CompileSession session = new CompileSession(myEnvironment, myFileNameTransformer); session.addSources(new LightVirtualFile("script" + LocalTimeCounter.currentTime() + ".kt", JetLanguage.INSTANCE, code)); - if (!session.analyze(myErrorStream) && !ignoreErrors) { + if (!session.analyze(myErrorStream, myMessageRenderer) && !ignoreErrors) { return null; } @@ -370,7 +375,7 @@ public class CompileEnvironment { ensureRuntime(); - if (!session.analyze(myErrorStream) && !ignoreErrors) { + if (!session.analyze(myErrorStream, myMessageRenderer) && !ignoreErrors) { return false; } diff --git a/compiler/cli/src/org/jetbrains/jet/compiler/CompileSession.java b/compiler/cli/src/org/jetbrains/jet/compiler/CompileSession.java index 641376b40b4d6a24ff78bbbe1232f58d9e29f526..7a63ab18823b7b5893f784a35344a2169fda489e 100644 --- a/compiler/cli/src/org/jetbrains/jet/compiler/CompileSession.java +++ b/compiler/cli/src/org/jetbrains/jet/compiler/CompileSession.java @@ -34,6 +34,7 @@ import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.descriptors.ModuleDescriptor; import org.jetbrains.jet.lang.diagnostics.Diagnostic; import org.jetbrains.jet.lang.diagnostics.DiagnosticFactory; +import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils; import org.jetbrains.jet.lang.diagnostics.Severity; import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.resolve.BindingContext; @@ -41,8 +42,7 @@ import org.jetbrains.jet.lang.resolve.java.AnalyzerFacade; import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver; import org.jetbrains.jet.plugin.JetFileType; -import java.io.File; -import java.io.PrintStream; +import java.io.*; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -84,11 +84,11 @@ public class CompileSession { VirtualFile vFile = myEnvironment.getLocalFileSystem().findFileByPath(path); if (vFile == null) { - myErrors.add("ERROR: File/directory not found: " + path); + myErrors.add("File/directory not found: " + path); return; } if (!vFile.isDirectory() && vFile.getFileType() != JetFileType.INSTANCE) { - myErrors.add("ERROR: Not a Kotlin file: " + path); + myErrors.add("Not a Kotlin file: " + path); return; } @@ -135,40 +135,26 @@ public class CompileSession { return mySourceFiles; } - public boolean analyze(final PrintStream out) { - if (!myErrors.isEmpty()) { - for (String error : myErrors) { - out.println(error); - } - return false; - } - - final ErrorCollector errorCollector = new ErrorCollector(); + public boolean analyze(@NotNull PrintStream out, @NotNull MessageRenderer renderer) { + MessageCollector collector = new MessageCollector(renderer); - reportSyntaxErrors(errorCollector); - analyzeAndReportSemanticErrors(errorCollector); + for (String error : myErrors) { + collector.report(Severity.ERROR, error, null, -1, -1); + } + reportSyntaxErrors(collector); + analyzeAndReportSemanticErrors(collector); - boolean hasIncompleteHierarchyErrors; - Collection incompletes = myBindingContext.getKeys(BindingContext.INCOMPLETE_HIERARCHY); - if (!incompletes.isEmpty()) { - out.println(Severity.ERROR + ":: The following classes have incomplete hierarchies:"); - for (ClassDescriptor incomplete : incompletes) { - out.println(Severity.ERROR + ":: " + fqName(incomplete)); - } - hasIncompleteHierarchyErrors = true; - } else { - hasIncompleteHierarchyErrors = false; - } + collector.printTo(out); - errorCollector.flushTo(out); - return !errorCollector.hasErrors() && !hasIncompleteHierarchyErrors; + return !collector.hasErrors(); } /** * @see JetTypeMapper#getFQName(DeclarationDescriptor) + * TODO possibly duplicates DescriptorUtils#getFQName(DeclarationDescriptor) */ - private String fqName(ClassOrNamespaceDescriptor descriptor) { + private static String fqName(ClassOrNamespaceDescriptor descriptor) { DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); if (containingDeclaration == null || containingDeclaration instanceof ModuleDescriptor || containingDeclaration.getName().equals(JavaDescriptorResolver.JAVA_ROOT)) { return descriptor.getName(); @@ -177,18 +163,31 @@ public class CompileSession { } } - private void analyzeAndReportSemanticErrors(ErrorCollector errorCollector) { + private void analyzeAndReportSemanticErrors(MessageCollector collector) { Predicate filesToAnalyzeCompletely = stubs ? Predicates.alwaysFalse() : Predicates.alwaysTrue(); myBindingContext = AnalyzerFacade.analyzeFilesWithJavaIntegration( myEnvironment.getProject(), mySourceFiles, filesToAnalyzeCompletely, JetControlFlowDataTraceFactory.EMPTY); for (Diagnostic diagnostic : myBindingContext.getDiagnostics()) { - errorCollector.report(diagnostic); + reportDiagnostic(collector, diagnostic); + } + + reportIncompleteHierarchies(collector); + } + + private void reportIncompleteHierarchies(MessageCollector collector) { + Collection incompletes = myBindingContext.getKeys(BindingContext.INCOMPLETE_HIERARCHY); + if (!incompletes.isEmpty()) { + StringBuilder message = new StringBuilder("The following classes have incomplete hierarchies:\n"); + for (ClassDescriptor incomplete : incompletes) { + message.append(" ").append(fqName(incomplete)).append("\n"); + } + collector.report(Severity.ERROR, message.toString(), null, -1, -1); } } - private void reportSyntaxErrors(final ErrorCollector errorCollector) { + private void reportSyntaxErrors(final MessageCollector messageCollector) { for (JetFile file : mySourceFiles) { file.accept(new PsiRecursiveElementWalkingVisitor() { @Override @@ -196,12 +195,19 @@ public class CompileSession { String description = element.getErrorDescription(); String message = StringUtil.isEmpty(description) ? "Syntax error" : description; Diagnostic diagnostic = DiagnosticFactory.create(Severity.ERROR, message).on(element); - errorCollector.report(diagnostic); + reportDiagnostic(messageCollector, diagnostic); } }); } } + 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(); + collector.report(diagnostic.getSeverity(), diagnostic.getMessage(), path, lineAndColumn.getLine(), lineAndColumn.getColumn()); + } + @NotNull public ClassFileFactory generate() { Project project = myEnvironment.getProject(); diff --git a/compiler/cli/src/org/jetbrains/jet/compiler/ErrorCollector.java b/compiler/cli/src/org/jetbrains/jet/compiler/MessageCollector.java similarity index 51% rename from compiler/cli/src/org/jetbrains/jet/compiler/ErrorCollector.java rename to compiler/cli/src/org/jetbrains/jet/compiler/MessageCollector.java index 8bf506a613b4d6af23111333de41372d923db8e6..397716a7c598c8e88fe22f9c8ec4a50bd8c84d65 100644 --- a/compiler/cli/src/org/jetbrains/jet/compiler/ErrorCollector.java +++ b/compiler/cli/src/org/jetbrains/jet/compiler/MessageCollector.java @@ -18,9 +18,8 @@ package org.jetbrains.jet.compiler; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimap; -import com.intellij.psi.PsiFile; -import org.jetbrains.jet.lang.diagnostics.Diagnostic; -import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.diagnostics.Severity; import java.io.PrintStream; @@ -29,27 +28,28 @@ import java.util.Collection; /** * @author alex.tkachman */ -class ErrorCollector { - private final Multimap maps = LinkedHashMultimap.create(); +/*package*/ class MessageCollector { + // File path (nullable) -> error message + private final Multimap groupedMessages = LinkedHashMultimap.create(); + private final MessageRenderer renderer; private boolean hasErrors; - public ErrorCollector() { + public MessageCollector(@NotNull MessageRenderer renderer) { + this.renderer = renderer; } - public void report(Diagnostic diagnostic) { - hasErrors |= diagnostic.getSeverity() == Severity.ERROR; - maps.put(diagnostic.getPsiFile(), diagnostic); + public void report(@NotNull Severity severity, @NotNull String message, @Nullable String path, int line, int column) { + hasErrors |= severity == Severity.ERROR; + groupedMessages.put(path, renderer.render(severity, message, path, line, column)); } - public void flushTo(final PrintStream out) { - if(!maps.isEmpty()) { - for (PsiFile psiFile : maps.keySet()) { - String path = psiFile.getVirtualFile().getPath(); - Collection diagnostics = maps.get(psiFile); - for (Diagnostic diagnostic : diagnostics) { - String position = DiagnosticUtils.formatPosition(diagnostic); - out.println(diagnostic.getSeverity().toString() + ": " + path + ":" + position + " " + diagnostic.getMessage()); + public void printTo(@NotNull PrintStream out) { + if (!groupedMessages.isEmpty()) { + for (String path : groupedMessages.keySet()) { + Collection diagnostics = groupedMessages.get(path); + for (String diagnostic : diagnostics) { + out.println(diagnostic); } } } diff --git a/compiler/cli/src/org/jetbrains/jet/compiler/MessageRenderer.java b/compiler/cli/src/org/jetbrains/jet/compiler/MessageRenderer.java new file mode 100644 index 0000000000000000000000000000000000000000..4573f3d77eb2f05d1e0ffab68eacf44c9dabfeb5 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/jet/compiler/MessageRenderer.java @@ -0,0 +1,76 @@ +/* + * Copyright 2000-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; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.diagnostics.Severity; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * @author abreslav + */ +public interface MessageRenderer { + + MessageRenderer TAGS = new MessageRenderer() { + private String renderWithStringSeverity(String severityString, String message, String path, int line, int column) { + StringBuilder out = new StringBuilder(); + out.append("<").append(severityString); + if (path != null) { + out.append(" path=\"").append(path).append("\""); + out.append(" line=\"").append(line).append("\""); + out.append(" column=\"").append(column).append("\""); + } + out.append(">\n"); + + out.append(message); + + out.append("\n"); + return out.toString(); + } + + @Override + public String render(@NotNull Severity severity, @NotNull String message, @Nullable String path, int line, int column) { + return renderWithStringSeverity(severity.toString(), message, path, line, column); + } + + @Override + public String renderException(@NotNull Throwable e) { + return renderWithStringSeverity("EXCEPTION", PLAIN.renderException(e), null, -1, -1); + } + }; + + MessageRenderer PLAIN = new MessageRenderer() { + @Override + public String render(@NotNull Severity severity, @NotNull String message, @Nullable String path, int line, int column) { + String position = path == null ? "" : path + ": (" + (line + ", " + column) + ") "; + return severity + ": " + position + message; + } + + @Override + public String renderException(@NotNull Throwable e) { + StringWriter out = new StringWriter(); + e.printStackTrace(new PrintWriter(out)); + return out.toString(); + } + }; + + String render(@NotNull Severity severity, @NotNull String message, @Nullable String path, int line, int column); + String renderException(@NotNull Throwable e); +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticUtils.java index a06b0c9a3a05431819a41dbbc0ec1a0aecb11e66..1a1a6b655ad61cab61738e0dde77ebc7e0cf0f7c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticUtils.java @@ -43,7 +43,7 @@ public class DiagnosticUtils { if (element != null) { return atLocation(element.getContainingFile(), element.getTextRange()); } - return "' at offset " + startOffset + " (line and file unknown)"; + return "' at offset " + startOffset + " (line and file unknown: no PSI element)"; } @Nullable @@ -63,7 +63,7 @@ public class DiagnosticUtils { @NotNull public static String atLocation(@NotNull PsiFile file, @NotNull TextRange textRange) { - Document document = file.getViewProvider().getDocument();//PsiDocumentManager.getInstance(file.getProject()).getDocument(file); + Document document = file.getViewProvider().getDocument(); return atLocation(file, textRange, document); } @@ -72,31 +72,28 @@ public class DiagnosticUtils { int offset = textRange.getStartOffset(); VirtualFile virtualFile = file.getVirtualFile(); String pathSuffix = virtualFile == null ? "" : " in " + virtualFile.getPath(); - if (document != null) { - int lineNumber = document.getLineNumber(offset); - int lineStartOffset = document.getLineStartOffset(lineNumber); - int column = offset - lineStartOffset; - - return "' at line " + (lineNumber + 1) + ":" + (column + 1) + pathSuffix; - } - else { - return "' at offset " + offset + " (line unknown)" + pathSuffix; - } + return offsetToLineAndColumn(document, offset).toString() + pathSuffix; } - public static String formatPosition(Diagnostic diagnostic) { + @NotNull + public static LineAndColumn getLineAndColumn(@NotNull Diagnostic diagnostic) { PsiFile file = diagnostic.getPsiFile(); Document document = file.getViewProvider().getDocument(); TextRange firstRange = diagnostic.getTextRanges().iterator().next(); - int offset = firstRange.getStartOffset(); - if (document != null) { - int lineNumber = document.getLineNumber(offset); - int lineStartOffset = document.getLineStartOffset(lineNumber); - int column = offset - lineStartOffset; + return offsetToLineAndColumn(document, firstRange.getStartOffset()); + } - return "(" + (lineNumber + 1) + "," + (column + 1) + ")"; + @NotNull + public static LineAndColumn offsetToLineAndColumn(Document document, int offset) { + if (document == null) { + return new LineAndColumn(-1, offset); } - return "(offset: " + offset + " line unknown)"; + + int lineNumber = document.getLineNumber(offset); + int lineStartOffset = document.getLineStartOffset(lineNumber); + int column = offset - lineStartOffset; + + return new LineAndColumn(lineNumber + 1, column + 1); } public static void throwIfRunningOnServer(Throwable e) { @@ -111,4 +108,31 @@ public class DiagnosticUtils { throw new RuntimeException(e); } } + + public static final class LineAndColumn { + private final int line; + private final int column; + + public LineAndColumn(int line, int column) { + this.line = line; + this.column = column; + } + + public int getLine() { + return line; + } + + public int getColumn() { + return column; + } + + // NOTE: This method is used for presenting positions to the user + @Override + public String toString() { + if (line < 0) { + return "(offset: " + column + " line unknown)"; + } + return "(" + line + "," + column + ")"; + } + } } diff --git a/compiler/tests/compiler-tests.iml b/compiler/tests/compiler-tests.iml index e3a58fc3f6ec9c475f002f319f292204fa46888a..eee45ec38be9128c473ed1d41505b0cb7b0dc769 100644 --- a/compiler/tests/compiler-tests.iml +++ b/compiler/tests/compiler-tests.iml @@ -13,6 +13,7 @@ + diff --git a/compiler/tests/org/jetbrains/jet/codegen/ForTestCompileStdlib.java b/compiler/tests/org/jetbrains/jet/codegen/ForTestCompileStdlib.java index 422c9720536d30e4d6eeb7557626af93e1964a55..12c982495dfd090af361c2a7702f1b6078fcf08d 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/ForTestCompileStdlib.java +++ b/compiler/tests/org/jetbrains/jet/codegen/ForTestCompileStdlib.java @@ -99,7 +99,7 @@ public class ForTestCompileStdlib { private static void compileKotlinPartOfStdlib(File destdir) throws IOException { // lame - KotlinCompiler.exec("-output", destdir.getPath(), "-src", "./stdlib/ktSrc", "-stdlib", destdir.getAbsolutePath()); + KotlinCompiler.exec(System.err, "-output", destdir.getPath(), "-src", "./stdlib/ktSrc", "-stdlib", destdir.getAbsolutePath()); } private static List javaFilesInDir(File dir) { diff --git a/compiler/tests/org/jetbrains/jet/codegen/TestlibTest.java b/compiler/tests/org/jetbrains/jet/codegen/TestlibTest.java index c64bc07a62b1b5315d02e7a67f6fbd0180f522c8..2336c3e013db97bf5aae2c5f2b137deda97821b6 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/TestlibTest.java +++ b/compiler/tests/org/jetbrains/jet/codegen/TestlibTest.java @@ -22,6 +22,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jetbrains.jet.compiler.CompileSession; +import org.jetbrains.jet.compiler.MessageRenderer; import org.jetbrains.jet.lang.descriptors.ClassDescriptor; import org.jetbrains.jet.lang.psi.JetClass; import org.jetbrains.jet.lang.psi.JetDeclaration; @@ -84,7 +85,7 @@ public class TestlibTest extends CodegenTestCase { session.addSources(localFileSystem.findFileByPath(JetParsingTest.getTestDataDir() + "/../../testlib/test")); session.addSources(localFileSystem.findFileByPath(JetParsingTest.getTestDataDir() + "/../../kunit/src")); - if (!session.analyze(System.err)) { + if (!session.analyze(System.err, MessageRenderer.PLAIN)) { throw new RuntimeException("There were compilation errors"); } diff --git a/idea/src/org/jetbrains/jet/plugin/compiler/JetCompiler.java b/idea/src/org/jetbrains/jet/plugin/compiler/JetCompiler.java index bd42098adecae5073a8ec2e1032fbeec98e6ac3e..04c0a6bebe7b4e66373114bcfbd78b7fe8808df6 100644 --- a/idea/src/org/jetbrains/jet/plugin/compiler/JetCompiler.java +++ b/idea/src/org/jetbrains/jet/plugin/compiler/JetCompiler.java @@ -37,6 +37,8 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Chunk; import com.intellij.util.SystemProperties; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.diagnostics.Severity; import org.jetbrains.jet.plugin.JetFileType; import java.io.*; @@ -199,10 +201,10 @@ public class JetCompiler implements TranslatingCompiler { try { String line = reader.readLine(); if (line == null) break; - listener.onTextAvailable(new ProcessEvent(NullProcessHandler.INSTANCE, line), ProcessOutputTypes.STDOUT); + listener.onTextAvailable(new ProcessEvent(NullProcessHandler.INSTANCE, line + "\n"), ProcessOutputTypes.STDERR); } catch (IOException e) { // Can't be - throw new RuntimeException(e); + throw new IllegalStateException(e); } } @@ -218,7 +220,7 @@ public class JetCompiler implements TranslatingCompiler { Class kompiler = Class.forName(compilerClassName, true, loader); Method exec = kompiler.getDeclaredMethod("exec", PrintStream.class, String[].class); - String[] arguments = { "-module", scriptFile.getAbsolutePath(), "-output", path(outputDir) }; + String[] arguments = { "-module", scriptFile.getAbsolutePath(), "-output", path(outputDir), "-tags" }; context.addMessage(INFORMATION, "Using kotlinHome=" + kotlinHome, "", -1, -1); context.addMessage(INFORMATION, "Invoking in-process compiler " + compilerClassName + " with arguments " + Arrays.asList(arguments), "", -1, -1); @@ -228,7 +230,7 @@ public class JetCompiler implements TranslatingCompiler { return ((Integer) rc).intValue(); } else { - throw new RuntimeException("Unexpected return: " + rc); + throw new IllegalStateException("Unexpected return: " + rc); } } catch (Throwable e) { LOG.error(e); @@ -267,6 +269,7 @@ public class JetCompiler implements TranslatingCompiler { params.setMainClass("org.jetbrains.jet.cli.KotlinCompiler"); params.getProgramParametersList().add("-module", scriptFile.getAbsolutePath()); params.getProgramParametersList().add("-output", path(outputDir)); + params.getProgramParametersList().add("-tags"); for (File jar : kompilerClasspath(kotlinHome, compileContext)) { params.getClassPath().add(jar); @@ -302,77 +305,7 @@ public class JetCompiler implements TranslatingCompiler { } private static ProcessAdapter createProcessListener(final CompileContext compileContext) { - return new ProcessAdapter() { - StringBuilder stderr = null; - - @Override - public void onTextAvailable(ProcessEvent event, Key outputType) { - String text = event.getText(); - String levelCode = parsePrefix(text); - if (outputType == ProcessOutputTypes.STDERR) { - if (stderr == null) { - stderr = new StringBuilder(); - } - stderr.append(text); - return; - } - if (levelCode != null) { - CompilerMessageCategory category = categories.get(levelCode); - text = text.substring(levelCode.length()); - - String path = ""; - int line = -1; - int column = -1; - int colonIndex = text.indexOf(':'); - if (text.startsWith(":")) { - text = text.substring(1); - } else if (colonIndex > 0) { - path = "file://" + text.substring(0, colonIndex).trim(); - text = text.substring(colonIndex + 1); - - Pattern position = Pattern.compile("\\((\\d+),\\s*(\\d+)\\)"); - - Matcher matcher = position.matcher(text); - if (matcher.find()) { - line = Integer.parseInt(matcher.group(1)); - column = Integer.parseInt(matcher.group(2)); - text = text.substring(matcher.group(0).length()); - } - } - - compileContext.addMessage(category, text, path, line, column); - } - else { - compileContext.addMessage(INFORMATION, text, "", -1, -1); - } - } - - @Override - public void processTerminated(ProcessEvent event) { - if (event.getExitCode() != 0) { - compileContext.addMessage(ERROR, "Compiler terminated with exit code: " + event.getExitCode(), "", -1, -1); - } - // By alex.tkachman: - if (stderr != null) { - compileContext.addMessage(ERROR, "stderr output:\r\n" + stderr.toString(), "", -1, -1); - } - } - }; - } - - private static String[] messagePrefixes = new String[] {"ERROR:", "WARNING:", "INFO:"} ; - private static Map categories = new HashMap(); - static { - categories.put("ERROR:", ERROR); - categories.put("WARNING:", WARNING); - categories.put("INFORMATION:", INFORMATION); - } - - private static String parsePrefix(String message) { - for (String prefix : messagePrefixes) { - if (message.startsWith(prefix)) return prefix; - } - return null; + return new CompilerProcessListener(compileContext); } private static String path(VirtualFile root) { @@ -406,4 +339,167 @@ public class JetCompiler implements TranslatingCompiler { throw new UnsupportedOperationException("getProcessInput is not implemented"); } } + + private static class CompilerProcessListener extends ProcessAdapter { + private static final Pattern DIAGNOSTIC_PATTERN = Pattern.compile("<(ERROR|WARNING|INFO|EXCEPTION)", Pattern.MULTILINE); + private static final Pattern OPEN_TAG_END_PATTERN = Pattern.compile(">", Pattern.MULTILINE | Pattern.DOTALL); + private static final Pattern ATTRIBUTE_PATTERN = Pattern.compile("\\s*(path|line|column)\\s*=\\s*\"(.*?)\"", Pattern.MULTILINE | Pattern.DOTALL); + private static final Pattern MESSAGE_PATTERN = Pattern.compile("(.*?)", Pattern.MULTILINE | Pattern.DOTALL); + + private enum State { + WAITING, ATTRIBUTES, MESSAGE + } + + private static class CompilerMessage { + private CompilerMessageCategory messageCategory; + private boolean isException; + private @Nullable String url; + private @Nullable Integer line; + private @Nullable Integer column; + private String message; + + public void setMessageCategoryFromString(String tagName) { + boolean exception = "EXCEPTION".equals(tagName); + if (Severity.ERROR.toString().equals(tagName) || exception) { + messageCategory = ERROR; + isException = exception; + } + else if (Severity.WARNING.toString().equals(tagName)) { + messageCategory = WARNING; + } + else { + messageCategory = INFORMATION; + } + } + + public void setAttributeFromStrings(String name, String value) { + if ("path".equals(name)) { + url = "file://" + value.trim(); + } + else if ("line".equals(name)) { + line = safeParseInt(value); + } + else if ("column".equals(name)) { + column = safeParseInt(value); + } + } + + @Nullable + private static Integer safeParseInt(String value) { + try { + return Integer.parseInt(value.trim()); + } + catch (NumberFormatException e) { + return null; + } + } + + public void setMessage(String message) { + this.message = message; + } + + public void reportTo(CompileContext compileContext) { + compileContext.addMessage(messageCategory, message, url == null ? "" : url, line == null ? -1 : line, column == null ? -1 : column); + if (isException) { + LOG.error(message); + } + } + } + + private final CompileContext compileContext; + private final StringBuilder output = new StringBuilder(); + private int firstUnprocessedIndex = 0; + private State state = State.WAITING; + private CompilerMessage currentCompilerMessage; + + public CompilerProcessListener(CompileContext compileContext) { + this.compileContext = compileContext; + } + + @Override + public void onTextAvailable(ProcessEvent event, Key outputType) { + String text = event.getText(); + if (outputType == ProcessOutputTypes.STDERR) { + output.append(text); + + // We loop until the state stabilizes + State lastState; + do { + lastState = state; + switch (state) { + case WAITING: { + Matcher matcher = matcher(DIAGNOSTIC_PATTERN); + if (find(matcher)) { + currentCompilerMessage = new CompilerMessage(); + currentCompilerMessage.setMessageCategoryFromString(matcher.group(1)); + state = State.ATTRIBUTES; + } + break; + } + case ATTRIBUTES: { + Matcher matcher = matcher(ATTRIBUTE_PATTERN); + int indexDelta = 0; + while (matcher.find()) { + handleSkippedOutput(output.subSequence(firstUnprocessedIndex + indexDelta, firstUnprocessedIndex + matcher.start())); + currentCompilerMessage.setAttributeFromStrings(matcher.group(1), matcher.group(2)); + indexDelta = matcher.end(); + + } + firstUnprocessedIndex += indexDelta; + + Matcher endMatcher = matcher(OPEN_TAG_END_PATTERN); + if (find(endMatcher)) { + state = State.MESSAGE; + } + break; + } + case MESSAGE: { + Matcher matcher = matcher(MESSAGE_PATTERN); + if (find(matcher)) { + currentCompilerMessage.setMessage(matcher.group(1)); + currentCompilerMessage.reportTo(compileContext); + state = State.WAITING; + } + break; + } + } + } + while (state != lastState); + + } + else { + compileContext.addMessage(INFORMATION, text, "", -1, -1); + } + } + + private boolean find(Matcher matcher) { + boolean result = matcher.find(); + if (result) { + handleSkippedOutput(output.subSequence(firstUnprocessedIndex, firstUnprocessedIndex + matcher.start())); + firstUnprocessedIndex += matcher.end(); + } + return result; + } + + private Matcher matcher(Pattern pattern) { + return pattern.matcher(output.subSequence(firstUnprocessedIndex, output.length())); + } + + @Override + public void processTerminated(ProcessEvent event) { + if (firstUnprocessedIndex < output.length()) { + handleSkippedOutput(output.substring(firstUnprocessedIndex).trim()); + } + if (event.getExitCode() != 0) { + compileContext.addMessage(ERROR, "Compiler terminated with exit code: " + event.getExitCode(), "", -1, -1); + } + } + + private void handleSkippedOutput(CharSequence substring) { + String message = substring.toString(); + if (!message.trim().isEmpty()) { + compileContext.addMessage(ERROR, message, "", -1, -1); + } + } + } } diff --git a/idea/src/org/jetbrains/jet/plugin/liveTemplates/JetLiveTemplateCompletionContributor.java b/idea/src/org/jetbrains/jet/plugin/liveTemplates/JetLiveTemplateCompletionContributor.java index 9b843f235f7b3eac184dec910132abff3c4a92c3..e7388dcc7d1543d43997f0553e2595557f29dd53 100644 --- a/idea/src/org/jetbrains/jet/plugin/liveTemplates/JetLiveTemplateCompletionContributor.java +++ b/idea/src/org/jetbrains/jet/plugin/liveTemplates/JetLiveTemplateCompletionContributor.java @@ -46,6 +46,9 @@ public class JetLiveTemplateCompletionContributor extends CompletionContributor protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull final CompletionResultSet result) { + if (parameters.getInvocationCount() == 0) { + return; + } final PsiFile file = parameters.getPosition().getContainingFile(); final int offset = parameters.getOffset(); final List templates = listApplicableTemplates(file, offset); diff --git a/idea/src/org/jetbrains/jet/plugin/liveTemplates/macro/JetSuggestVariableNameMacro.java b/idea/src/org/jetbrains/jet/plugin/liveTemplates/macro/JetSuggestVariableNameMacro.java index 2bb9ebdcd176825ba60d40bd51b2f90aa51a044c..1e0183f1776f8e72aa6bae00ca94fdf619307579 100644 --- a/idea/src/org/jetbrains/jet/plugin/liveTemplates/macro/JetSuggestVariableNameMacro.java +++ b/idea/src/org/jetbrains/jet/plugin/liveTemplates/macro/JetSuggestVariableNameMacro.java @@ -30,7 +30,7 @@ import org.jetbrains.jet.plugin.JetBundle; public class JetSuggestVariableNameMacro extends Macro { @Override public String getName() { - return "suggestVariableName"; + return "kotlinSuggestVariableName"; } @Override diff --git a/lib/asm-all-3.3.1.jar b/lib/asm-all-3.3.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..df03b326614ffafd517a4ecba14054855f21a9b2 Binary files /dev/null and b/lib/asm-all-3.3.1.jar differ diff --git a/lib/asm-util-3.3.1.jar b/lib/asm-util-3.3.1.jar deleted file mode 100644 index 0230bbcfb71bf24e7ac9b49e32a84b7eceaeb5a9..0000000000000000000000000000000000000000 Binary files a/lib/asm-util-3.3.1.jar and /dev/null differ