diff --git a/js/js.frontend/src/org/jetbrains/k2js/resolve/diagnostics/DefaultErrorMessagesJs.kt b/js/js.frontend/src/org/jetbrains/k2js/resolve/diagnostics/DefaultErrorMessagesJs.kt index 1d45ff3e86e..85e44c29c56 100644 --- a/js/js.frontend/src/org/jetbrains/k2js/resolve/diagnostics/DefaultErrorMessagesJs.kt +++ b/js/js.frontend/src/org/jetbrains/k2js/resolve/diagnostics/DefaultErrorMessagesJs.kt @@ -29,6 +29,9 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by Delegates.lazy { put(ErrorsJs.NATIVE_INDEXER_KEY_SHOULD_BE_STRING_OR_NUMBER, "Native {0}''s first parameter type should be ''kotlin.String'' or subtype of ''kotlin.Number''", Renderers.STRING) put(ErrorsJs.NATIVE_GETTER_RETURN_TYPE_SHOULD_BE_NULLABLE, "Native getter''s return type should be nullable") put(ErrorsJs.NATIVE_INDEXER_WRONG_PARAMETER_COUNT, "Expected {0} parameters for native {1}", Renderers.TO_STRING, Renderers.STRING) + put(ErrorsJs.JSCODE_ERROR, "JavaScript: {0}", Renderers.TO_STRING, Renderers.TO_STRING) + put(ErrorsJs.JSCODE_WARNING, "JavaScript: {0}", Renderers.TO_STRING, Renderers.TO_STRING) + put(ErrorsJs.JSCODE_ARGUMENT_SHOULD_BE_LITERAL, "Argument must be string literal") this } diff --git a/js/js.frontend/src/org/jetbrains/k2js/resolve/diagnostics/ErrorsJs.java b/js/js.frontend/src/org/jetbrains/k2js/resolve/diagnostics/ErrorsJs.java index c5986d07dbf..88d4c69e468 100644 --- a/js/js.frontend/src/org/jetbrains/k2js/resolve/diagnostics/ErrorsJs.java +++ b/js/js.frontend/src/org/jetbrains/k2js/resolve/diagnostics/ErrorsJs.java @@ -16,24 +16,29 @@ package org.jetbrains.k2js.resolve.diagnostics; -import org.jetbrains.jet.lang.diagnostics.DiagnosticFactory0; -import org.jetbrains.jet.lang.diagnostics.DiagnosticFactory1; -import org.jetbrains.jet.lang.diagnostics.DiagnosticFactory2; -import org.jetbrains.jet.lang.diagnostics.Errors; +import com.intellij.openapi.util.TextRange; +import org.jetbrains.jet.lang.diagnostics.*; import org.jetbrains.jet.lang.psi.JetDeclaration; import org.jetbrains.jet.lang.psi.JetElement; +import org.jetbrains.jet.lang.psi.JetExpression; import org.jetbrains.jet.lang.types.JetType; +import java.util.List; + +import static org.jetbrains.jet.lang.diagnostics.PositioningStrategies.CALL_EXPRESSION; import static org.jetbrains.jet.lang.diagnostics.PositioningStrategies.DECLARATION_RETURN_TYPE; import static org.jetbrains.jet.lang.diagnostics.PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT; import static org.jetbrains.jet.lang.diagnostics.Severity.ERROR; +import static org.jetbrains.jet.lang.diagnostics.Severity.WARNING; public interface ErrorsJs { - DiagnosticFactory1 NATIVE_ANNOTATIONS_ALLOWED_ONLY_ON_MEMBER_OR_EXTENSION_FUN = DiagnosticFactory1.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT); DiagnosticFactory1 NATIVE_INDEXER_KEY_SHOULD_BE_STRING_OR_NUMBER = DiagnosticFactory1.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT); DiagnosticFactory0 NATIVE_GETTER_RETURN_TYPE_SHOULD_BE_NULLABLE = DiagnosticFactory0.create(ERROR, DECLARATION_RETURN_TYPE); DiagnosticFactory2 NATIVE_INDEXER_WRONG_PARAMETER_COUNT = DiagnosticFactory2.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT); + DiagnosticFactory2> JSCODE_ERROR = DiagnosticFactory2.create(ERROR, JsCodePositioningStrategy.INSTANCE$); + DiagnosticFactory2> JSCODE_WARNING = DiagnosticFactory2.create(WARNING, JsCodePositioningStrategy.INSTANCE$); + DiagnosticFactory0 JSCODE_ARGUMENT_SHOULD_BE_LITERAL = DiagnosticFactory0.create(ERROR, CALL_EXPRESSION); @SuppressWarnings("UnusedDeclaration") Object _initializer = new Object() { diff --git a/js/js.frontend/src/org/jetbrains/k2js/resolve/diagnostics/JsPositioningStrategies.kt b/js/js.frontend/src/org/jetbrains/k2js/resolve/diagnostics/JsPositioningStrategies.kt new file mode 100644 index 00000000000..353a080cfd0 --- /dev/null +++ b/js/js.frontend/src/org/jetbrains/k2js/resolve/diagnostics/JsPositioningStrategies.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2010-2014 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.k2js.resolve.diagnostics + +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiElement +import org.jetbrains.jet.lang.diagnostics.Diagnostic +import org.jetbrains.jet.lang.diagnostics.DiagnosticWithParameters1 +import org.jetbrains.jet.lang.diagnostics.PositioningStrategy +import org.jetbrains.jet.lang.psi.JetExpression +import org.jetbrains.jet.renderer.Renderer +import org.jetbrains.jet.lang.diagnostics.DiagnosticWithParameters2 +import org.jetbrains.jet.lang.diagnostics.ParametrizedDiagnostic + +public object JsCodePositioningStrategy : PositioningStrategy() { + override fun markDiagnostic(diagnostic: ParametrizedDiagnostic): List { + [suppress("UNCHECKED_CAST")] + val diagnosticWithParameters = diagnostic as DiagnosticWithParameters2> + return diagnosticWithParameters.getB() + } +} \ No newline at end of file diff --git a/js/js.parser/src/com/google/gwt/dev/js/UncheckedJsParserException.java b/js/js.parser/src/com/google/gwt/dev/js/AbortParsingException.java similarity index 62% rename from js/js.parser/src/com/google/gwt/dev/js/UncheckedJsParserException.java rename to js/js.parser/src/com/google/gwt/dev/js/AbortParsingException.java index a88462c8bb4..666adce3fde 100644 --- a/js/js.parser/src/com/google/gwt/dev/js/UncheckedJsParserException.java +++ b/js/js.parser/src/com/google/gwt/dev/js/AbortParsingException.java @@ -15,16 +15,9 @@ */ package com.google.gwt.dev.js; -// An unchecked wrapper exception to interop with Rhino. -class UncheckedJsParserException extends RuntimeException { - - private final JsParserException parserException; - - public UncheckedJsParserException(JsParserException parserException) { - this.parserException = parserException; - } - - public JsParserException getParserException() { - return parserException; - } +/** + * Used only to exit parser on error + */ +public class AbortParsingException extends RuntimeException { + public AbortParsingException() {} } diff --git a/js/js.parser/src/com/google/gwt/dev/js/JsParser.java b/js/js.parser/src/com/google/gwt/dev/js/JsParser.java index e00f8fef789..863d1bba353 100644 --- a/js/js.parser/src/com/google/gwt/dev/js/JsParser.java +++ b/js/js.parser/src/com/google/gwt/dev/js/JsParser.java @@ -79,9 +79,6 @@ public class JsParser { popScope(); return stmts; } - catch (UncheckedJsParserException e) { - throw e.getParserException(); - } finally { Context.exit(); } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallExpressionTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallExpressionTranslator.java index 31545834914..bbf3c9b8bc9 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallExpressionTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallExpressionTranslator.java @@ -21,30 +21,37 @@ import com.google.dart.compiler.backend.js.ast.metadata.MetadataPackage; import com.google.dart.compiler.common.SourceInfoImpl; import com.google.gwt.dev.js.JsParser; import com.google.gwt.dev.js.JsParserException; +import com.google.gwt.dev.js.AbortParsingException; +import com.google.gwt.dev.js.rhino.ErrorReporter; +import com.google.gwt.dev.js.rhino.EvaluatorException; +import com.intellij.openapi.util.TextRange; import org.jetbrains.jet.lang.descriptors.*; -import org.jetbrains.jet.lang.psi.JetCallExpression; -import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.diagnostics.DiagnosticFactory2; +import org.jetbrains.jet.lang.diagnostics.ParametrizedDiagnostic; +import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage; import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall; import org.jetbrains.jet.lang.types.lang.InlineStrategy; import org.jetbrains.jet.lang.types.lang.InlineUtil; -import org.jetbrains.jet.lang.psi.ValueArgument; import org.jetbrains.k2js.translate.callTranslator.CallTranslator; import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.intrinsic.functions.patterns.DescriptorPredicate; import org.jetbrains.k2js.translate.intrinsic.functions.patterns.PatternBuilder; +import org.jetbrains.k2js.resolve.diagnostics.ErrorsJs; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.k2js.translate.utils.BindingUtils; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import static com.google.gwt.dev.js.rhino.Utils.isEndOfLine; import static org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage.getFunctionResolvedCallWithAssert; +import static org.jetbrains.k2js.translate.utils.BindingUtils.getCompileTimeValue; public final class CallExpressionTranslator extends AbstractCallExpressionTranslator { @@ -126,13 +133,17 @@ public final class CallExpressionTranslator extends AbstractCallExpressionTransl private JsNode translateJsCode() { List arguments = expression.getValueArguments(); JetExpression argumentExpression = arguments.get(0).getArgumentExpression(); - assert argumentExpression != null; + + if (!(argumentExpression instanceof JetStringTemplateExpression)) { + context().getTrace().report(ErrorsJs.JSCODE_ARGUMENT_SHOULD_BE_LITERAL.on(expression)); + return program().getEmptyExpression(); + } List statements = parseJsCode(argumentExpression); int size = statements.size(); if (size == 0) { - return program().getEmptyStatement(); + return program().getEmptyExpression(); } else if (size > 1) { return new JsBlock(statements); } else { @@ -158,6 +169,9 @@ public final class CallExpressionTranslator extends AbstractCallExpressionTransl JsScope scope = context().scope(); StringReader reader = new StringReader((String) jsCode); statements.addAll(JsParser.parse(info, scope, reader, errorReporter, /* insideFunction= */ true)); + } catch (AbortParsingException e) { + /** @see JsCodeErrorReporter#error */ + return Collections.emptyList(); } catch (JsParserException e) { throw new RuntimeException(e); } catch (IOException e) { @@ -170,12 +184,22 @@ public final class CallExpressionTranslator extends AbstractCallExpressionTransl private class JsCodeErrorReporter implements ErrorReporter { @NotNull private final JetExpression jsCodeExpression; + private JsCodeErrorReporter(@NotNull JetExpression expression) { jsCodeExpression = expression; } @Override public void error(String message, String sourceName, int line, String lineSource, int lineOffset) { + ParametrizedDiagnostic diagnostic = getDiagnostic(ErrorsJs.JSCODE_ERROR, message, line, lineOffset); + context().getTrace().report(diagnostic); + throw new AbortParsingException(); + } + + @Override + public void warning(String message, String sourceName, int line, String lineSource, int lineOffset) { + ParametrizedDiagnostic diagnostic = getDiagnostic(ErrorsJs.JSCODE_WARNING, message, line, lineOffset); + context().getTrace().report(diagnostic); } /** @@ -188,11 +212,50 @@ public final class CallExpressionTranslator extends AbstractCallExpressionTransl throw new RuntimeException(message); } + private ParametrizedDiagnostic getDiagnostic( + @NotNull DiagnosticFactory2> diagnosticFactory, + String message, + int line, + int lineOffset + ) { + String text = (String) getCompileTimeValue(bindingContext(), jsCodeExpression); + int offset = jsCodeExpression.getTextOffset() + offsetFromStart(text, line, lineOffset); + + assert jsCodeExpression instanceof JetStringTemplateExpression: "js argument is expected to be compile-time string literal"; + int quotesLength = jsCodeExpression.getFirstChild().getTextLength(); + offset += quotesLength; + + TextRange textRange = new TextRange(offset, offset + 1); + return diagnosticFactory.on(jsCodeExpression, message, Collections.singletonList(textRange)); + } + /** - * Do not report warnings + * Calculates an offset from the start of a text for a position, + * defined by line and offset in that line. */ - @Override - public void warning(String message, String sourceName, int line, String lineSource, int lineOffset) { + private int offsetFromStart(String text, int line, int offset) { + int i = 0; + int lineCount = 0; + int offsetInLine = 0; + + while (i < text.length()) { + char c = text.charAt(i); + + if (lineCount == line && offsetInLine == offset) { + return i; + } + + if (isEndOfLine(c)) { + offsetInLine = 0; + lineCount++; + assert lineCount <= line; + } + + i++; + offsetInLine++; + } + + return text.length(); } } }