blob: 2c48b817e3a2b3282c3ea56ac5378d99a504ea55 [file] [log] [blame]
/*
* Copyright 2016 Google Inc. All Rights Reserved.
*
* 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.turbine.parse;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import com.google.common.base.Joiner;
import com.google.turbine.diag.SourceFile;
import com.google.turbine.diag.TurbineError;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for best-effort parser error handling. */
@RunWith(JUnit4.class)
public class ParseErrorTest {
@Test
public void intBound() {
StreamLexer lexer =
new StreamLexer(
new UnicodeEscapePreprocessor(new SourceFile("<>", String.valueOf("2147483648"))));
ConstExpressionParser parser = new ConstExpressionParser(lexer, lexer.next(), lexer.position());
TurbineError e = assertThrows(TurbineError.class, () -> parser.expression());
assertThat(e).hasMessageThat().contains("invalid literal");
}
@Test
public void hexIntBound() {
StreamLexer lexer =
new StreamLexer(
new UnicodeEscapePreprocessor(new SourceFile("<>", String.valueOf("0x100000000"))));
ConstExpressionParser parser = new ConstExpressionParser(lexer, lexer.next(), lexer.position());
TurbineError e = assertThrows(TurbineError.class, () -> parser.expression());
assertThat(e).hasMessageThat().contains("invalid literal");
}
@Test
public void unexpectedTopLevel() {
String input = "public static void main(String[] args) {}";
TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
assertThat(e)
.hasMessageThat()
.isEqualTo(
lines(
"<>:1: error: unexpected token: void",
"public static void main(String[] args) {}",
" ^"));
}
@Test
public void unexpectedIdentifier() {
String input = "public clas Test {}";
TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
assertThat(e)
.hasMessageThat()
.isEqualTo(
lines(
"<>:1: error: unexpected identifier 'clas'", //
"public clas Test {}",
" ^"));
}
@Test
public void missingTrailingCloseBrace() {
String input = "public class Test {\n\n";
TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
assertThat(e)
.hasMessageThat()
.isEqualTo(
lines(
"<>:2: error: unexpected end of input", //
"",
"^"));
}
@Test
public void annotationArgument() {
String input = "@A(x = System.err.println()) class Test {}\n";
TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
assertThat(e)
.hasMessageThat()
.isEqualTo(
lines(
"<>:1: error: invalid annotation argument", //
"@A(x = System.err.println()) class Test {}",
" ^"));
}
@Test
public void dropParens() {
String input = "enum E { ONE(";
TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
assertThat(e)
.hasMessageThat()
.isEqualTo(
lines(
"<>:1: error: unexpected end of input", //
"enum E { ONE(",
" ^"));
}
@Test
public void dropBlocks() {
String input = "class T { Object f = new Object() {";
TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
assertThat(e)
.hasMessageThat()
.isEqualTo(
lines(
"<>:1: error: unexpected end of input", //
"class T { Object f = new Object() {",
" ^"));
}
@Test
public void unterminatedString() {
String input = "class T { String s = \"hello\nworld\"; }";
TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
assertThat(e)
.hasMessageThat()
.isEqualTo(
lines(
"<>:1: error: unterminated string literal", //
"class T { String s = \"hello",
" ^"));
}
@Test
public void emptyChar() {
String input = "class T { char c = ''; }";
TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
assertThat(e)
.hasMessageThat()
.isEqualTo(
lines(
"<>:1: error: empty char literal", //
"class T { char c = ''; }",
" ^"));
}
@Test
public void unterminatedChar() {
String input = "class T { char c = '; }";
TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
assertThat(e)
.hasMessageThat()
.isEqualTo(
lines(
"<>:1: error: unterminated char literal", //
"class T { char c = '; }",
" ^"));
}
@Test
public void unterminatedExpr() {
String input = "class T { String s = hello + world }";
TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
assertThat(e)
.hasMessageThat()
.isEqualTo(
lines(
"<>:1: error: unterminated expression, expected ';' not found", //
"class T { String s = hello + world }",
" ^"));
}
@Test
public void abruptMultivariableDeclaration() {
String input = "class T { int x,; }";
TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
assertThat(e)
.hasMessageThat()
.isEqualTo(
lines(
"<>:1: error: expected token <identifier>", //
"class T { int x,; }",
" ^"));
}
@Test
public void invalidAnnotation() {
String input = "@Foo(x = @E [] x) class T {}";
TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
assertThat(e)
.hasMessageThat()
.isEqualTo(
lines(
"<>:1: error: invalid annotation argument", //
"@Foo(x = @E [] x) class T {}",
" ^"));
}
@Test
public void unclosedComment() {
String input = "/** *\u001a/ class Test {}";
TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
assertThat(e)
.hasMessageThat()
.isEqualTo(
lines(
"<>:1: error: unclosed comment", //
"/** *\u001a/ class Test {}",
"^"));
}
@Test
public void unclosedGenerics() {
String input = "enum\te{l;p u@.<@";
TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
assertThat(e)
.hasMessageThat()
.isEqualTo(
lines(
"<>:1: error: unexpected end of input", //
"enum\te{l;p u@.<@",
" ^"));
}
@Test
public void arrayDot() {
String input = "enum\te{p;ullt[].<~>>>L\0";
TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
assertThat(e)
.hasMessageThat()
.isEqualTo(
lines(
"<>:1: error: unexpected token: <", //
"enum\te{p;ullt[].<~>>>L\0",
" ^"));
}
@Test
public void implementsBeforeExtends() {
String input = "class T implements A extends B {}";
TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
assertThat(e)
.hasMessageThat()
.isEqualTo(
lines(
"<>:1: error: 'extends' must come before 'implements'",
"class T implements A extends B {}",
" ^"));
}
@Test
public void unpairedSurrogate() {
String input = "import pkg\uD800.PackageTest;";
TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
assertThat(e)
.hasMessageThat()
.isEqualTo(
lines(
"<>:1: error: unpaired surrogate 0xd800",
"import pkg\uD800.PackageTest;",
" ^"));
}
@Test
public void abruptSurrogate() {
String input = "import pkg\uD800";
TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
assertThat(e)
.hasMessageThat()
.isEqualTo(
lines("<>:1: error: unpaired surrogate 0xd800", "import pkg\uD800", " ^"));
}
@Test
public void unexpectedSurrogate() {
String input = "..\uD800\uDC00";
TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
assertThat(e)
.hasMessageThat()
.isEqualTo(
lines(
"<>:1: error: unexpected input: U+10000", //
"..\uD800\uDC00",
" ^"));
}
@Test
public void notCast() {
String input = "@j(@truetugt^(oflur)!%t";
TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input));
assertThat(e)
.hasMessageThat()
.isEqualTo(
lines(
"<>:1: error: could not evaluate constant expression",
"@j(@truetugt^(oflur)!%t",
" ^"));
}
private static String lines(String... lines) {
return Joiner.on(System.lineSeparator()).join(lines);
}
}