RhinoErrorReporter.java
/*
* Copyright 2009 The Closure Compiler Authors.
*
* 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 com.google.javascript.jscomp;
import com.google.common.collect.ImmutableMap;
import com.google.javascript.rhino.ErrorReporter;
import com.google.javascript.rhino.SimpleErrorReporter;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;
/**
* An error reporter for serializing Rhino errors into our error format.
* @author nicksantos@google.com (Nick Santos)
*/
class RhinoErrorReporter {
static final DiagnosticType PARSE_ERROR =
DiagnosticType.error("JSC_PARSE_ERROR", "Parse error. {0}");
static final DiagnosticType TYPE_PARSE_ERROR =
DiagnosticType.warning("JSC_TYPE_PARSE_ERROR", "{0}");
static final DiagnosticType UNRECOGNIZED_TYPE_ERROR =
DiagnosticType.warning("JSC_UNRECOGNIZED_TYPE_ERROR", "{0}");
// This is separate from TYPE_PARSE_ERROR because there are many instances of this warning
// and it is unfeasible to fix them all right away.
static final DiagnosticType JSDOC_MISSING_BRACES_WARNING =
DiagnosticType.disabled("JSC_JSDOC_MISSING_BRACES_WARNING", "{0}");
// This is separate from TYPE_PARSE_ERROR because there are many instances of this warning
// and it is unfeasible to fix them all right away.
static final DiagnosticType JSDOC_MISSING_TYPE_WARNING =
DiagnosticType.disabled("JSC_JSDOC_MISSING_TYPE_WARNING", "{0}");
static final DiagnosticType TOO_MANY_TEMPLATE_PARAMS =
DiagnosticType.disabled("JSC_TOO_MANY_TEMPLATE_PARAMS", "{0}");
// Special-cased errors, so that they can be configured via the
// warnings API.
static final DiagnosticType TRAILING_COMMA =
DiagnosticType.error("JSC_TRAILING_COMMA",
"Parse error. IE8 (and below) will parse trailing commas in " +
"array and object literals incorrectly. " +
"If you are targeting newer versions of JS, " +
"set the appropriate language_in option.");
static final DiagnosticType DUPLICATE_PARAM =
DiagnosticType.error("JSC_DUPLICATE_PARAM", "Parse error. {0}");
static final DiagnosticType UNNECESSARY_ESCAPE =
DiagnosticType.disabled("JSC_UNNECESSARY_ESCAPE", "Parse error. {0}");
static final DiagnosticType INVALID_PARAM =
DiagnosticType.warning("JSC_INVALID_PARAM", "Parse error. {0}");
static final DiagnosticType BAD_JSDOC_ANNOTATION =
DiagnosticType.warning("JSC_BAD_JSDOC_ANNOTATION", "Parse error. {0}");
static final DiagnosticType JSDOC_IN_BLOCK_COMMENT =
DiagnosticType.warning("JSC_JSDOC_IN_BLOCK_COMMENT", "Parse error. {0}");
static final DiagnosticType INVALID_ES3_PROP_NAME = DiagnosticType.warning(
"JSC_INVALID_ES3_PROP_NAME",
"Keywords and reserved words are not allowed as unquoted property " +
"names in older versions of JavaScript. " +
"If you are targeting newer versions of JavaScript, " +
"set the appropriate language_in option.");
static final DiagnosticType PARSE_TREE_TOO_DEEP =
DiagnosticType.error("PARSE_TREE_TOO_DEEP",
"Parse tree too deep.");
static final DiagnosticType INVALID_OCTAL_LITERAL =
DiagnosticType.warning("INVALID_OCTAL_LITERAL",
"This style of octal literal is not supported in strict mode.");
static final DiagnosticType STRING_CONTINUATION =
DiagnosticType.warning("JSC_STRING_CONTINUATION", "{0}");
static final DiagnosticType ES6_FEATURE =
DiagnosticType.error("ES6_FEATURE",
"{0}. Use --language_in=ECMASCRIPT6 or ECMASCRIPT6_STRICT " +
"or higher to enable ES6 features.");
static final DiagnosticType ES6_TYPED =
DiagnosticType.error("ES6_TYPED",
"{0}. Use --language_in=ECMASCRIPT6_TYPED to enable ES6 typed features.");
static final DiagnosticType MISPLACED_TYPE_SYNTAX =
DiagnosticType.error("MISPLACED_TYPE_SYNTAX",
"Can only have JSDoc or inline type annotations, not both");
// A map of Rhino messages to their DiagnosticType.
private final Map<Pattern, DiagnosticType> typeMap;
final AbstractCompiler compiler;
/**
* For each message such as "Not a good use of {0}", replace the place
* holder {0} with a wild card that matches all possible strings.
* Also put the any non-place-holder in quotes for regex matching later.
*/
private static Pattern replacePlaceHolders(String s) {
s = Pattern.quote(s);
return Pattern.compile(s.replaceAll("\\{\\d+\\}", "\\\\E.*\\\\Q"));
}
private RhinoErrorReporter(AbstractCompiler compiler) {
this.compiler = compiler;
typeMap =
ImmutableMap.<Pattern, DiagnosticType>builder()
// Trailing comma
.put(
Pattern.compile("Trailing comma is not legal in an ECMA-262 object initializer"),
TRAILING_COMMA)
// Duplicate parameter
.put(replacePlaceHolders("Duplicate parameter name \"{0}\""), DUPLICATE_PARAM)
.put(Pattern.compile("Unnecessary escape:.*"), UNNECESSARY_ESCAPE)
.put(Pattern.compile("^invalid param name.*"), INVALID_PARAM)
// Unknown @annotations.
.put(
replacePlaceHolders(SimpleErrorReporter.getMessage0("msg.bad.jsdoc.tag")),
BAD_JSDOC_ANNOTATION)
.put(
Pattern.compile("^" + Pattern.quote(
"Non-JSDoc comment has annotations. "
+ "Did you mean to start it with '/**'?")),
JSDOC_IN_BLOCK_COMMENT)
.put(
Pattern.compile(
"^Keywords and reserved words are not allowed as unquoted property.*"),
INVALID_ES3_PROP_NAME)
.put(Pattern.compile("^Too many template parameters"), TOO_MANY_TEMPLATE_PARAMS)
// Type annotation warnings.
.put(
Pattern.compile(".*Type annotations should have curly braces.*"),
JSDOC_MISSING_BRACES_WARNING)
.put(Pattern.compile("Missing type declaration\\."), JSDOC_MISSING_TYPE_WARNING)
// Unresolved types that aren't forward declared.
.put(Pattern.compile(".*Unknown type.*"), UNRECOGNIZED_TYPE_ERROR)
// Type annotation errors.
.put(Pattern.compile("^Bad type annotation.*"), TYPE_PARSE_ERROR)
// Parse tree too deep.
.put(Pattern.compile("Too deep recursion while parsing"), PARSE_TREE_TOO_DEEP)
// Old-style octal literals
.put(Pattern.compile("^Octal .*literal.*"), INVALID_OCTAL_LITERAL)
.put(Pattern.compile("^String continuations.*"), STRING_CONTINUATION)
.put(
Pattern.compile("^this language feature is only supported for ECMASCRIPT6 mode.*"),
ES6_FEATURE)
.put(Pattern.compile("^type syntax is only supported in ES6 typed mode.*"), ES6_TYPED)
.put(Pattern.compile("^Can only have JSDoc or inline type.*"), MISPLACED_TYPE_SYNTAX)
.build();
}
public static ErrorReporter forOldRhino(AbstractCompiler compiler) {
return new OldRhinoErrorReporter(compiler);
}
void warningAtLine(String message, String sourceName, int line,
int lineOffset) {
compiler.report(
makeError(message, sourceName, line, lineOffset, CheckLevel.WARNING));
}
void errorAtLine(String message, String sourceName, int line,
int lineOffset) {
compiler.report(
makeError(message, sourceName, line, lineOffset, CheckLevel.ERROR));
}
protected DiagnosticType mapError(String message) {
for (Entry<Pattern, DiagnosticType> entry : typeMap.entrySet()) {
if (entry.getKey().matcher(message).matches()) {
return entry.getValue();
}
}
return null;
}
private JSError makeError(String message, String sourceName, int line,
int lineOffset, CheckLevel defaultLevel) {
// Try to see if the message is one of the rhino errors we want to
// expose as DiagnosticType by matching it with the regex key.
DiagnosticType type = mapError(message);
if (type != null) {
return JSError.make(
sourceName, line, lineOffset, type, message);
}
return JSError.make(sourceName, line, lineOffset, defaultLevel,
PARSE_ERROR, message);
}
private static class OldRhinoErrorReporter extends RhinoErrorReporter
implements ErrorReporter {
private OldRhinoErrorReporter(AbstractCompiler compiler) {
super(compiler);
}
@Override
public void error(String message, String sourceName, int line,
int lineOffset) {
super.errorAtLine(message, sourceName, line, lineOffset);
}
@Override
public void warning(String message, String sourceName, int line,
int lineOffset) {
super.warningAtLine(message, sourceName, line, lineOffset);
}
}
}