mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-11-10 12:28:25 +00:00
Improve lexer and parser diagnostic locations (#7990)
Lexer diagnostics used a single `const char*` as their source location, which only allowed pointing at a single character rather than a run of characters. This limited the underlining of unrecognized tokens, and also made escape sequence diagnostics particularly strange by stopping at whatever point the lexer discovered the error and pointing only at that location. For example: `"\w"` was reported at the `w`, and `"\x"` at the closing `"`. This change tries to always keep the primary location of a diagnostic the point at which typing (inserting or deleting) might fix the error. So for `"\x"`, `\x` is underlined, but the position is at the `"` as that's where you would type a hex code. The lexer's emitter now takes a special lexing source location that represents either a single position _between_ two characters, a single character, or a range of the source buffer. The lexer diagnostics are updated to construct these for the character range as appropriate. Parser diagnostics for a missing token were located at the _next_ token, which is particularly confusing for terminators like missing `;`s. Now they are located at a position after the previous token, with or without a space depending on the token, but typically on the same line. Diagnostics still sort by how far the parser has read. The parser diagnostics for unclosed groups also now attaches a label to the opening token when it is on a separate line. And the diagnostics for mismatched indent in a multi-line string attaches a label to the closing `'''` that establishes that indent. There are likely more location improvements we should do here, but this seemed like a reasonable incremental improvement and is what Claude found when doing an initial audit. One example where I think we should make further improvements is around suggesting raw identifiers when we find an unexpected `Cpp` or `Core` or `base`. We do this consistently now, but raw identifiers should be rare. A TODO is added for these cases. Assisted-by: Claude Code --------- Co-authored-by: Richard Smith <richard@metafoo.co.uk>
This commit is contained in:
1 parent
611e6a210e
commit
68569d234d
173 files changed
+1782
-689
No files matched your search
+1
-3
@@ -12,8 +12,6 @@
|
||||
|
||||
namespace N;
|
||||
|
||||
// CHECK:STDERR: fail_base_as_declared_name.carbon:[[@LINE+4]]:6-9: error: `.` should be followed by a name [ExpectedDeclNameAfterPeriod]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_base_as_declared_name.carbon:[[@LINE+2]]:6-9: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR: fail_base_as_declared_name.carbon:[[@LINE+2]]:6-9: error: `.` should be followed by a name; prefix reserved word with `r#` to form a valid identifier [ExpectedDeclNameAfterPeriod]
|
||||
// CHECK:STDERR:
|
||||
fn N.base() {}
|
||||
@@ -86,12 +86,12 @@ extern library "extern_library_owner" fn F();
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_extern_library_mismatch_owner.carbon:[[@LINE+4]]:41: error: `import` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_extern_library_mismatch_owner.carbon:[[@LINE+2]]:1-40: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR:
|
||||
import library "extern_library_mismatch"
|
||||
|
||||
// CHECK:STDERR: fail_extern_library_mismatch_owner.carbon:[[@LINE+4]]:1-6: error: `import` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_extern_library_mismatch_owner.carbon:[[@LINE-4]]:1-40: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR:
|
||||
extern fn F();
|
||||
|
||||
// --- fail_extern_self_library.carbon
|
||||
|
||||
+1
-1
@@ -112,7 +112,7 @@ impl C as I {
|
||||
// This leaves the impl with a placeholder instruction in the witness table.
|
||||
// CHECK:STDERR: fail_invalid_fn_syntax_in_impl.carbon:[[@LINE+6]]:9: error: name `x` not found [NameNotFound]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_invalid_fn_syntax_in_impl.carbon:[[@LINE+4]]:11-14: error: expected `,` or `]` [UnexpectedTokenAfterListElement]
|
||||
// CHECK:STDERR: fail_invalid_fn_syntax_in_impl.carbon:[[@LINE+4]]:10: error: expected `,` or `]` [UnexpectedTokenAfterListElement]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_invalid_fn_syntax_in_impl.carbon:[[@LINE+2]]:8-18: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR:
|
||||
|
||||
+6
-6
@@ -49,11 +49,11 @@ class C {
|
||||
}
|
||||
|
||||
// TODO: This should typecheck (see #5366).
|
||||
// CHECK:STDERR: fail_todo_redeclaration_reenter_class.carbon:[[@LINE+6]]:9-10: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR: fail_todo_redeclaration_reenter_class.carbon:[[@LINE+6]]:9: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_redeclaration_reenter_class.carbon:[[@LINE+4]]:9-10: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_redeclaration_reenter_class.carbon:[[@LINE+2]]:15: error: expected `as` in `impl` declaration [ImplExpectedAs]
|
||||
// CHECK:STDERR: fail_todo_redeclaration_reenter_class.carbon:[[@LINE+2]]:14: error: expected `as` in `impl` declaration [ImplExpectedAs]
|
||||
// CHECK:STDERR:
|
||||
impl C.(as Z) {}
|
||||
|
||||
@@ -71,7 +71,7 @@ class C {
|
||||
// TODO: This should typecheck (see #5366).
|
||||
// CHECK:STDERR: fail_todo_redeclaration_reenter_class_explicit_self.carbon:[[@LINE+6]]:9-12: error: name `Self` not found [NameNotFound]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_redeclaration_reenter_class_explicit_self.carbon:[[@LINE+4]]:20: error: expected `as` in `impl` declaration [ImplExpectedAs]
|
||||
// CHECK:STDERR: fail_todo_redeclaration_reenter_class_explicit_self.carbon:[[@LINE+4]]:19: error: expected `as` in `impl` declaration [ImplExpectedAs]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_redeclaration_reenter_class_explicit_self.carbon:[[@LINE+2]]:1-21: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR:
|
||||
@@ -89,7 +89,7 @@ class C {
|
||||
// TODO: This should typecheck (see #5366).
|
||||
// CHECK:STDERR: fail_todo_redeclaration_reenter_class_mixed_self_outside.carbon:[[@LINE+6]]:9-12: error: name `Self` not found [NameNotFound]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_redeclaration_reenter_class_mixed_self_outside.carbon:[[@LINE+4]]:20: error: expected `as` in `impl` declaration [ImplExpectedAs]
|
||||
// CHECK:STDERR: fail_todo_redeclaration_reenter_class_mixed_self_outside.carbon:[[@LINE+4]]:19: error: expected `as` in `impl` declaration [ImplExpectedAs]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_redeclaration_reenter_class_mixed_self_outside.carbon:[[@LINE+2]]:1-21: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR:
|
||||
@@ -105,11 +105,11 @@ class C {
|
||||
}
|
||||
|
||||
// TODO: This should typecheck (see #5366).
|
||||
// CHECK:STDERR: fail_todo_redeclaration_reenter_class_mixed_self_inside.carbon:[[@LINE+6]]:9-10: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR: fail_todo_redeclaration_reenter_class_mixed_self_inside.carbon:[[@LINE+6]]:9: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_redeclaration_reenter_class_mixed_self_inside.carbon:[[@LINE+4]]:9-10: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_redeclaration_reenter_class_mixed_self_inside.carbon:[[@LINE+2]]:15: error: expected `as` in `impl` declaration [ImplExpectedAs]
|
||||
// CHECK:STDERR: fail_todo_redeclaration_reenter_class_mixed_self_inside.carbon:[[@LINE+2]]:14: error: expected `as` in `impl` declaration [ImplExpectedAs]
|
||||
// CHECK:STDERR:
|
||||
impl C.(as Z) {}
|
||||
|
||||
|
||||
+5
-5
@@ -21,11 +21,11 @@ class N.C {
|
||||
class D {}
|
||||
}
|
||||
|
||||
// CHECK:STDERR: fail_todo_reenter_class_scope.carbon:[[@LINE+6]]:11-12: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR: fail_todo_reenter_class_scope.carbon:[[@LINE+6]]:11: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_reenter_class_scope.carbon:[[@LINE+4]]:11-12: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_reenter_class_scope.carbon:[[@LINE+2]]:17: error: expected `as` in `impl` declaration [ImplExpectedAs]
|
||||
// CHECK:STDERR: fail_todo_reenter_class_scope.carbon:[[@LINE+2]]:16: error: expected `as` in `impl` declaration [ImplExpectedAs]
|
||||
// CHECK:STDERR:
|
||||
impl N.C.(as Z) {
|
||||
fn ZF() {
|
||||
@@ -49,7 +49,7 @@ class N.C {
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_reenter_class_scope_explicit_self.carbon:[[@LINE+6]]:19: error: name `Z` not found [NameNotFound]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_reenter_class_scope_explicit_self.carbon:[[@LINE+4]]:22: error: expected `as` in `impl` declaration [ImplExpectedAs]
|
||||
// CHECK:STDERR: fail_todo_reenter_class_scope_explicit_self.carbon:[[@LINE+4]]:21: error: expected `as` in `impl` declaration [ImplExpectedAs]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_reenter_class_scope_explicit_self.carbon:[[@LINE+2]]:1-22: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR:
|
||||
@@ -75,7 +75,7 @@ class N.C {
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_reenter_class_scope_unrelated_self.carbon:[[@LINE+6]]:16: error: name `Z` not found [NameNotFound]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_reenter_class_scope_unrelated_self.carbon:[[@LINE+4]]:19: error: expected `as` in `impl` declaration [ImplExpectedAs]
|
||||
// CHECK:STDERR: fail_todo_reenter_class_scope_unrelated_self.carbon:[[@LINE+4]]:18: error: expected `as` in `impl` declaration [ImplExpectedAs]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_reenter_class_scope_unrelated_self.carbon:[[@LINE+2]]:1-19: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR:
|
||||
@@ -99,7 +99,7 @@ class N.D {}
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_reenter_namespace_scope.carbon:[[@LINE+6]]:14: error: name `Z` not found [NameNotFound]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_reenter_namespace_scope.carbon:[[@LINE+4]]:17: error: expected `as` in `impl` declaration [ImplExpectedAs]
|
||||
// CHECK:STDERR: fail_todo_reenter_namespace_scope.carbon:[[@LINE+4]]:16: error: expected `as` in `impl` declaration [ImplExpectedAs]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_reenter_namespace_scope.carbon:[[@LINE+2]]:1-17: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR:
|
||||
|
||||
@@ -31,7 +31,7 @@ import Cpp inline '''c++
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_import_cpp_library_without_name_and_inline_without_content.carbon:[[@LINE+4]]:20-25: error: expected a string literal to specify the library name [ExpectedLibraryName]
|
||||
// CHECK:STDERR: fail_import_cpp_library_without_name_and_inline_without_content.carbon:[[@LINE+4]]:19: error: expected a string literal to specify the library name [ExpectedLibraryName]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_import_cpp_library_without_name_and_inline_without_content.carbon:[[@LINE+2]]:20-25: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR:
|
||||
@@ -41,7 +41,7 @@ import Cpp library inline;
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_import_cpp_library_with_name_and_inline_without_content.carbon:[[@LINE+4]]:28-33: error: `import` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR: fail_import_cpp_library_with_name_and_inline_without_content.carbon:[[@LINE+4]]:27: error: `import` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_import_cpp_library_with_name_and_inline_without_content.carbon:[[@LINE+2]]:1-34: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR:
|
||||
@@ -51,7 +51,7 @@ import Cpp library "foo.h" inline;
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_import_cpp_library_without_name_and_inline_with_content.carbon:[[@LINE+4]]:20-25: error: expected a string literal to specify the library name [ExpectedLibraryName]
|
||||
// CHECK:STDERR: fail_import_cpp_library_without_name_and_inline_with_content.carbon:[[@LINE+4]]:19: error: expected a string literal to specify the library name [ExpectedLibraryName]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_import_cpp_library_without_name_and_inline_with_content.carbon:[[@LINE+2]]:20-25: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR:
|
||||
@@ -61,7 +61,7 @@ import Cpp library inline "";
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_import_cpp_library_with_name_and_inline_with_content.carbon:[[@LINE+4]]:28-33: error: `import` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR: fail_import_cpp_library_with_name_and_inline_with_content.carbon:[[@LINE+4]]:27: error: `import` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_import_cpp_library_with_name_and_inline_with_content.carbon:[[@LINE+2]]:1-37: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR:
|
||||
|
||||
@@ -20,9 +20,7 @@ library "[[@TEST_NAME]]";
|
||||
|
||||
import Cpp library "header.h";
|
||||
|
||||
// CHECK:STDERR: fail_duplicate_cpp_name.carbon:[[@LINE+4]]:11-13: error: `namespace` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_duplicate_cpp_name.carbon:[[@LINE+2]]:11-13: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR: fail_duplicate_cpp_name.carbon:[[@LINE+2]]:11-13: error: `namespace` introducer should be followed by a name; prefix reserved word with `r#` to form a valid identifier [ExpectedDeclName]
|
||||
// CHECK:STDERR:
|
||||
namespace Cpp;
|
||||
|
||||
@@ -33,9 +31,7 @@ library "[[@TEST_NAME]]";
|
||||
import Cpp library "header.h";
|
||||
|
||||
namespace N;
|
||||
// CHECK:STDERR: fail_cpp_in_inner_namespace.carbon:[[@LINE+4]]:13-15: error: `.` should be followed by a name [ExpectedDeclNameAfterPeriod]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_cpp_in_inner_namespace.carbon:[[@LINE+2]]:13-15: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR: fail_cpp_in_inner_namespace.carbon:[[@LINE+2]]:13-15: error: `.` should be followed by a name; prefix reserved word with `r#` to form a valid identifier [ExpectedDeclNameAfterPeriod]
|
||||
// CHECK:STDERR:
|
||||
namespace N.Cpp;
|
||||
|
||||
@@ -55,9 +51,9 @@ library "[[@TEST_NAME]]";
|
||||
|
||||
import Cpp library "header.h";
|
||||
|
||||
// CHECK:STDERR: fail_add_name_to_cpp_namespace.carbon:[[@LINE+4]]:7-9: error: `class` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR: fail_add_name_to_cpp_namespace.carbon:[[@LINE+4]]:7-9: error: `class` introducer should be followed by a name; prefix reserved word with `r#` to form a valid identifier [ExpectedDeclName]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_add_name_to_cpp_namespace.carbon:[[@LINE+2]]:7-9: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR: fail_add_name_to_cpp_namespace.carbon:[[@LINE+2]]:7-9: error: name `r#Cpp` not found [NameNotFound]
|
||||
// CHECK:STDERR:
|
||||
class Cpp.C {};
|
||||
|
||||
|
||||
@@ -69,12 +69,12 @@ import Cpp library "prefix_inc_and_dec.h";
|
||||
|
||||
fn F() {
|
||||
var prefix: Cpp.Prefix = Cpp.Prefix.Prefix();
|
||||
// CHECK:STDERR: fail_prefix_calling_postfix.carbon:[[@LINE+4]]:9-10: error: expected `;` after expression statement [ExpectedExprSemi]
|
||||
// CHECK:STDERR: fail_prefix_calling_postfix.carbon:[[@LINE+4]]:9: error: expected `;` after expression statement [ExpectedExprSemi]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_prefix_calling_postfix.carbon:[[@LINE+2]]:3-11: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR:
|
||||
prefix++;
|
||||
// CHECK:STDERR: fail_prefix_calling_postfix.carbon:[[@LINE+2]]:9-10: error: expected `;` after expression statement [ExpectedExprSemi]
|
||||
// CHECK:STDERR: fail_prefix_calling_postfix.carbon:[[@LINE+2]]:9: error: expected `;` after expression statement [ExpectedExprSemi]
|
||||
// CHECK:STDERR:
|
||||
prefix--;
|
||||
}
|
||||
@@ -493,26 +493,26 @@ fn F() {
|
||||
var c: Cpp.UnsupportedUnaryC = Cpp.UnsupportedUnaryC.UnsupportedUnaryC();
|
||||
|
||||
// Arithmetic.
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_unary_operators.carbon:[[@LINE+6]]:37: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_unary_operators.carbon:[[@LINE+6]]:36: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_unary_operators.carbon:[[@LINE+4]]:37: error: whitespace missing after binary operator [BinaryOperatorRequiresWhitespace]
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_unary_operators.carbon:[[@LINE+4]]:37: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_unary_operators.carbon:[[@LINE+2]]:37: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_unary_operators.carbon:[[@LINE+2]]:38: error: whitespace missing after binary operator [BinaryOperatorRequiresWhitespace]
|
||||
// CHECK:STDERR:
|
||||
let plus: Cpp.UnsupportedUnaryC = +c;
|
||||
|
||||
// Increment and Decrement.
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_unary_operators.carbon:[[@LINE+2]]:4-5: error: expected `;` after expression statement [ExpectedExprSemi]
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_unary_operators.carbon:[[@LINE+2]]:4: error: expected `;` after expression statement [ExpectedExprSemi]
|
||||
// CHECK:STDERR:
|
||||
c++;
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_unary_operators.carbon:[[@LINE+2]]:4-5: error: expected `;` after expression statement [ExpectedExprSemi]
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_unary_operators.carbon:[[@LINE+2]]:4: error: expected `;` after expression statement [ExpectedExprSemi]
|
||||
// CHECK:STDERR:
|
||||
c--;
|
||||
|
||||
// Bitwise.
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_unary_operators.carbon:[[@LINE+4]]:42: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_unary_operators.carbon:[[@LINE+4]]:41: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_unary_operators.carbon:[[@LINE+2]]:42: error: `let` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR: fail_todo_import_unsupported_unary_operators.carbon:[[@LINE+2]]:41: error: `let` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR:
|
||||
let not_value: Cpp.UnsupportedUnaryC = ~c;
|
||||
|
||||
|
||||
+197
-8
@@ -75,31 +75,33 @@ let n: i32 = {};
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_prelude_as_namespace.carbon:[[@LINE+4]]:11-14: error: `namespace` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_prelude_as_namespace.carbon:[[@LINE+2]]:11-14: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR: fail_prelude_as_namespace.carbon:[[@LINE+2]]:11-14: error: `namespace` introducer should be followed by a name; prefix reserved word with `r#` to form a valid identifier [ExpectedDeclName]
|
||||
// CHECK:STDERR:
|
||||
namespace Core;
|
||||
|
||||
// CHECK:STDERR: fail_prelude_as_namespace.carbon:[[@LINE+2]]:4-7: error: `fn` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR: fail_prelude_as_namespace.carbon:[[@LINE+4]]:4-7: error: `fn` introducer should be followed by a name; prefix reserved word with `r#` to form a valid identifier [ExpectedDeclName]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_prelude_as_namespace.carbon:[[@LINE+2]]:30-33: warning: binding `N` unused [UnusedBinding]
|
||||
// CHECK:STDERR:
|
||||
fn Core.Int[T: type](generic N: T) -> {} { return {}; }
|
||||
|
||||
// Make sure use of `i32` doesn't crash after above errors.
|
||||
// CHECK:STDERR: fail_prelude_as_namespace.carbon:[[@LINE+2]]:15-17: error: `Core.Int` implicitly referenced here, but package `Core` not found [CoreNotFound]
|
||||
// CHECK:STDERR:
|
||||
let n: type = i32;
|
||||
|
||||
// --- fail_prelude_as_class.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_prelude_as_class.carbon:[[@LINE+4]]:7-10: error: `class` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_prelude_as_class.carbon:[[@LINE+2]]:7-10: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR: fail_prelude_as_class.carbon:[[@LINE+2]]:7-10: error: `class` introducer should be followed by a name; prefix reserved word with `r#` to form a valid identifier [ExpectedDeclName]
|
||||
// CHECK:STDERR:
|
||||
class Core {
|
||||
fn Int[T: type](generic N: T) -> {} { return {}; }
|
||||
fn Int[T: type](generic _: T) -> {} { return {}; }
|
||||
}
|
||||
|
||||
// CHECK:STDERR: fail_prelude_as_class.carbon:[[@LINE+2]]:15-17: error: `Core.Int` implicitly referenced here, but package `Core` not found [CoreNotFound]
|
||||
// CHECK:STDERR:
|
||||
let n: type = i32;
|
||||
|
||||
|
||||
@@ -699,5 +701,192 @@ let n: type = i32;
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- fail_prelude_as_namespace.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
|
||||
// CHECK:STDOUT: %.Self.frozen: type = symbolic_binding .Self [symbolic_self]
|
||||
// CHECK:STDOUT: %pattern_type.9a5: type = pattern_type type [concrete]
|
||||
// CHECK:STDOUT: %T.patt: %pattern_type.9a5 = symbolic_binding_pattern T, 0 [symbolic]
|
||||
// CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic]
|
||||
// CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic]
|
||||
// CHECK:STDOUT: %N.patt: %pattern_type.51d = symbolic_binding_pattern N, 1 [symbolic]
|
||||
// CHECK:STDOUT: %N: %T = symbolic_binding N, 1 [symbolic]
|
||||
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
|
||||
// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %.469: Core.Form = init_form %empty_struct_type [concrete]
|
||||
// CHECK:STDOUT: %pattern_type.a96: type = pattern_type %empty_struct_type [concrete]
|
||||
// CHECK:STDOUT: %return.param_patt: %pattern_type.a96 = out_param_pattern [concrete]
|
||||
// CHECK:STDOUT: %return.patt: %pattern_type.a96 = return_slot_pattern %return.param_patt, %empty_struct_type [concrete]
|
||||
// CHECK:STDOUT: %Int.type: type = fn_type @Int [concrete]
|
||||
// CHECK:STDOUT: %Int: %Int.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %n.patt: %pattern_type.9a5 = value_binding_pattern n [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
|
||||
// CHECK:STDOUT: .r#Core = %Core
|
||||
// CHECK:STDOUT: .n = %n
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %Core: <namespace> = namespace [concrete] {
|
||||
// CHECK:STDOUT: .Int = %Int.decl
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %Int.decl: %Int.type = fn_decl @Int [concrete = constants.%Int] {
|
||||
// CHECK:STDOUT: %T.patt.loc12_14.1: %pattern_type.9a5 = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc12_14.2 (constants.%T.patt)]
|
||||
// CHECK:STDOUT: %N.patt.loc12_31.1: @Int.%pattern_type (%pattern_type.51d) = symbolic_binding_pattern N, 1 [symbolic = %N.patt.loc12_31.2 (constants.%N.patt)]
|
||||
// CHECK:STDOUT: %return.param_patt: %pattern_type.a96 = out_param_pattern [concrete = constants.%return.param_patt]
|
||||
// CHECK:STDOUT: %return.patt: %pattern_type.a96 = return_slot_pattern %return.param_patt, %.loc12_40.2 [concrete = constants.%return.patt]
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %.loc12_40.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct]
|
||||
// CHECK:STDOUT: %.loc12_40.2: type = converted %.loc12_40.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
|
||||
// CHECK:STDOUT: %.loc12_40.3: Core.Form = init_form %.loc12_40.2 [concrete = constants.%.469]
|
||||
// CHECK:STDOUT: %.loc12_16.1: type = splice_block %.loc12_16.2 [concrete = type] {
|
||||
// CHECK:STDOUT: %.Self.frozen.loc12_14: type = symbolic_binding .Self [symbolic_self = constants.%.Self.frozen]
|
||||
// CHECK:STDOUT: %.loc12_16.2: type = type_literal type [concrete = type]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %T.loc12_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc12_14.1 (constants.%T)]
|
||||
// CHECK:STDOUT: %.loc12_33: type = splice_block %T.ref [symbolic = %T.loc12_14.1 (constants.%T)] {
|
||||
// CHECK:STDOUT: %.Self.frozen.loc12_31: type = symbolic_binding .Self [symbolic_self = constants.%.Self.frozen]
|
||||
// CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc12_14.2 [symbolic = %T.loc12_14.1 (constants.%T)]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %N.loc12_31.2: @Int.%T.loc12_14.1 (%T) = symbolic_binding N, 1 [symbolic = %N.loc12_31.1 (constants.%N)]
|
||||
// CHECK:STDOUT: %return.param: ref %empty_struct_type = out_param call_param0
|
||||
// CHECK:STDOUT: %return: ref %empty_struct_type = return_slot %return.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %.loc17: type = type_literal type [concrete = type]
|
||||
// CHECK:STDOUT: %n: type = wrapper_binding n, @__global_init.%.loc17 [concrete = <error>]
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %n.patt: %pattern_type.9a5 = value_binding_pattern n [concrete = constants.%n.patt]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: generic fn @Int(%T.loc12_14.2: type, %N.loc12_31.2: @Int.%T.loc12_14.1 (%T)) {
|
||||
// CHECK:STDOUT: %T.patt.loc12_14.2: %pattern_type.9a5 = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc12_14.2 (constants.%T.patt)]
|
||||
// CHECK:STDOUT: %T.loc12_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc12_14.1 (constants.%T)]
|
||||
// CHECK:STDOUT: %pattern_type: type = pattern_type %T.loc12_14.1 [symbolic = %pattern_type (constants.%pattern_type.51d)]
|
||||
// CHECK:STDOUT: %N.patt.loc12_31.2: @Int.%pattern_type (%pattern_type.51d) = symbolic_binding_pattern N, 1 [symbolic = %N.patt.loc12_31.2 (constants.%N.patt)]
|
||||
// CHECK:STDOUT: %N.loc12_31.1: @Int.%T.loc12_14.1 (%T) = symbolic_binding N, 1 [symbolic = %N.loc12_31.1 (constants.%N)]
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !definition:
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn() -> out %return.param: %empty_struct_type {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %.loc12_52.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct]
|
||||
// CHECK:STDOUT: %.loc12_52.2: init %empty_struct_type = struct_init () [concrete = constants.%empty_struct]
|
||||
// CHECK:STDOUT: %.loc12_53: init %empty_struct_type = converted %.loc12_52.1, %.loc12_52.2 [concrete = constants.%empty_struct]
|
||||
// CHECK:STDOUT: return %.loc12_53
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @__global_init() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %.loc17: type = type_literal <error> [concrete = <error>]
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: specific @Int(constants.%T, constants.%N) {
|
||||
// CHECK:STDOUT: %T.patt.loc12_14.2 => constants.%T.patt
|
||||
// CHECK:STDOUT: %T.loc12_14.1 => constants.%T
|
||||
// CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d
|
||||
// CHECK:STDOUT: %N.patt.loc12_31.2 => constants.%N.patt
|
||||
// CHECK:STDOUT: %N.loc12_31.1 => constants.%N
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- fail_prelude_as_class.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
|
||||
// CHECK:STDOUT: %Core: type = class_type @Core [concrete]
|
||||
// CHECK:STDOUT: %.Self.frozen: type = symbolic_binding .Self [symbolic_self]
|
||||
// CHECK:STDOUT: %pattern_type.9a5: type = pattern_type type [concrete]
|
||||
// CHECK:STDOUT: %T.patt: %pattern_type.9a5 = symbolic_binding_pattern T, 0 [symbolic]
|
||||
// CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic]
|
||||
// CHECK:STDOUT: %pattern_type.51d: type = pattern_type %T [symbolic]
|
||||
// CHECK:STDOUT: %_.patt: %pattern_type.51d = symbolic_binding_pattern _, 1 [symbolic]
|
||||
// CHECK:STDOUT: %_: %T = symbolic_binding _, 1 [symbolic]
|
||||
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
|
||||
// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %.469: Core.Form = init_form %empty_struct_type [concrete]
|
||||
// CHECK:STDOUT: %pattern_type.a96: type = pattern_type %empty_struct_type [concrete]
|
||||
// CHECK:STDOUT: %return.param_patt: %pattern_type.a96 = out_param_pattern [concrete]
|
||||
// CHECK:STDOUT: %return.patt: %pattern_type.a96 = return_slot_pattern %return.param_patt, %empty_struct_type [concrete]
|
||||
// CHECK:STDOUT: %Core.Int.type: type = fn_type @Core.Int [concrete]
|
||||
// CHECK:STDOUT: %Core.Int: %Core.Int.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
|
||||
// CHECK:STDOUT: %n.patt: %pattern_type.9a5 = value_binding_pattern n [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
|
||||
// CHECK:STDOUT: .r#Core = %Core.decl
|
||||
// CHECK:STDOUT: .n = %n
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %Core.decl: type = class_decl @Core [concrete = constants.%Core] {} {}
|
||||
// CHECK:STDOUT: %.loc12: type = type_literal type [concrete = type]
|
||||
// CHECK:STDOUT: %n: type = wrapper_binding n, @__global_init.%.loc12 [concrete = <error>]
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %n.patt: %pattern_type.9a5 = value_binding_pattern n [concrete = constants.%n.patt]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Core {
|
||||
// CHECK:STDOUT: %Core.Int.decl: %Core.Int.type = fn_decl @Core.Int [concrete = constants.%Core.Int] {
|
||||
// CHECK:STDOUT: %T.patt.loc7_11.1: %pattern_type.9a5 = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc7_11.2 (constants.%T.patt)]
|
||||
// CHECK:STDOUT: %_.patt.loc7_28.1: @Core.Int.%pattern_type (%pattern_type.51d) = symbolic_binding_pattern _, 1 [symbolic = %_.patt.loc7_28.2 (constants.%_.patt)]
|
||||
// CHECK:STDOUT: %return.param_patt: %pattern_type.a96 = out_param_pattern [concrete = constants.%return.param_patt]
|
||||
// CHECK:STDOUT: %return.patt: %pattern_type.a96 = return_slot_pattern %return.param_patt, %.loc7_37.2 [concrete = constants.%return.patt]
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %.loc7_37.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct]
|
||||
// CHECK:STDOUT: %.loc7_37.2: type = converted %.loc7_37.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
|
||||
// CHECK:STDOUT: %.loc7_37.3: Core.Form = init_form %.loc7_37.2 [concrete = constants.%.469]
|
||||
// CHECK:STDOUT: %.loc7_13.1: type = splice_block %.loc7_13.2 [concrete = type] {
|
||||
// CHECK:STDOUT: %.Self.frozen.loc7_11: type = symbolic_binding .Self [symbolic_self = constants.%.Self.frozen]
|
||||
// CHECK:STDOUT: %.loc7_13.2: type = type_literal type [concrete = type]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %T.loc7_11.2: type = symbolic_binding T, 0 [symbolic = %T.loc7_11.1 (constants.%T)]
|
||||
// CHECK:STDOUT: %.loc7_30: type = splice_block %T.ref [symbolic = %T.loc7_11.1 (constants.%T)] {
|
||||
// CHECK:STDOUT: %.Self.frozen.loc7_28: type = symbolic_binding .Self [symbolic_self = constants.%.Self.frozen]
|
||||
// CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc7_11.2 [symbolic = %T.loc7_11.1 (constants.%T)]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %_.loc7_28.2: @Core.Int.%T.loc7_11.1 (%T) = symbolic_binding _, 1 [symbolic = %_.loc7_28.1 (constants.%_)]
|
||||
// CHECK:STDOUT: %return.param: ref %empty_struct_type = out_param call_param0
|
||||
// CHECK:STDOUT: %return: ref %empty_struct_type = return_slot %return.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type]
|
||||
// CHECK:STDOUT: complete_type_witness = %complete_type
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .Self = constants.%Core
|
||||
// CHECK:STDOUT: .Int = %Core.Int.decl
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: generic fn @Core.Int(%T.loc7_11.2: type, %_.loc7_28.2: @Core.Int.%T.loc7_11.1 (%T)) {
|
||||
// CHECK:STDOUT: %T.patt.loc7_11.2: %pattern_type.9a5 = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc7_11.2 (constants.%T.patt)]
|
||||
// CHECK:STDOUT: %T.loc7_11.1: type = symbolic_binding T, 0 [symbolic = %T.loc7_11.1 (constants.%T)]
|
||||
// CHECK:STDOUT: %pattern_type: type = pattern_type %T.loc7_11.1 [symbolic = %pattern_type (constants.%pattern_type.51d)]
|
||||
// CHECK:STDOUT: %_.patt.loc7_28.2: @Core.Int.%pattern_type (%pattern_type.51d) = symbolic_binding_pattern _, 1 [symbolic = %_.patt.loc7_28.2 (constants.%_.patt)]
|
||||
// CHECK:STDOUT: %_.loc7_28.1: @Core.Int.%T.loc7_11.1 (%T) = symbolic_binding _, 1 [symbolic = %_.loc7_28.1 (constants.%_)]
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !definition:
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn() -> out %return.param: %empty_struct_type {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %.loc7_49.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct]
|
||||
// CHECK:STDOUT: %.loc7_49.2: init %empty_struct_type = struct_init () [concrete = constants.%empty_struct]
|
||||
// CHECK:STDOUT: %.loc7_50: init %empty_struct_type = converted %.loc7_49.1, %.loc7_49.2 [concrete = constants.%empty_struct]
|
||||
// CHECK:STDOUT: return %.loc7_50
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @__global_init() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %.loc12: type = type_literal <error> [concrete = <error>]
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: specific @Core.Int(constants.%T, constants.%_) {
|
||||
// CHECK:STDOUT: %T.patt.loc7_11.2 => constants.%T.patt
|
||||
// CHECK:STDOUT: %T.loc7_11.1 => constants.%T
|
||||
// CHECK:STDOUT: %pattern_type => constants.%pattern_type.51d
|
||||
// CHECK:STDOUT: %_.patt.loc7_28.2 => constants.%_.patt
|
||||
// CHECK:STDOUT: %_.loc7_28.1 => constants.%_
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
+2
-2
@@ -84,9 +84,9 @@ class C {
|
||||
|
||||
fn F() -> {} {
|
||||
let _: {} = {};
|
||||
// CHECK:STDERR: fail_use.carbon:[[@LINE+6]]:10: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR: fail_use.carbon:[[@LINE+6]]:9: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_use.carbon:[[@LINE+4]]:10: error: `return` statements must end with a `;` [ExpectedStatementSemi]
|
||||
// CHECK:STDERR: fail_use.carbon:[[@LINE+4]]:9: error: `return` statements must end with a `;` [ExpectedStatementSemi]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_use.carbon:[[@LINE+2]]:10: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
|
||||
// CHECK:STDERR:
|
||||
|
||||
@@ -163,7 +163,11 @@ struct Loc {
|
||||
int32_t column_number = -1;
|
||||
|
||||
// The number of bytes the location covers in `line`, starting at
|
||||
// `column_number`. Should always be at least 1.
|
||||
// `column_number`.
|
||||
//
|
||||
// Zero names the point just before `column_number`, where something missing
|
||||
// would be written. That point can be just past the end of `line`, so
|
||||
// `column_number` can be one more than the line's length.
|
||||
//
|
||||
// This is the range underlined in the source snippet. A message's own
|
||||
// location is a point that an editor puts a cursor on, so where a range was
|
||||
|
||||
@@ -98,6 +98,39 @@ inline auto IsDiagnostic(
|
||||
testing::Field("labels", &Diagnostics::Diagnostic::labels, labels));
|
||||
}
|
||||
|
||||
// The start and length expected of a location, in `IsDiagnosticWithRange`.
|
||||
struct ExpectedLoc {
|
||||
testing::Matcher<int> line_number;
|
||||
testing::Matcher<int> column_number;
|
||||
testing::Matcher<int> length;
|
||||
};
|
||||
|
||||
// Matches a diagnostic with one message and one range attached to it with no
|
||||
// text, checking the lengths of the message's location and of the range as
|
||||
// well as their starts.
|
||||
inline auto IsDiagnosticWithRange(testing::Matcher<Diagnostics::Kind> kind,
|
||||
testing::Matcher<Diagnostics::Level> level,
|
||||
ExpectedLoc loc,
|
||||
testing::Matcher<std::string> message,
|
||||
ExpectedLoc range)
|
||||
-> testing::Matcher<Diagnostics::Diagnostic> {
|
||||
return IsDiagnostic(
|
||||
level,
|
||||
testing::AllOf(
|
||||
IsDiagnosticMessage(kind, level, loc.line_number, loc.column_number,
|
||||
message),
|
||||
testing::Field(
|
||||
"loc", &Diagnostics::Message::loc,
|
||||
testing::Field("length", &Diagnostics::Loc::length, loc.length))),
|
||||
testing::IsEmpty(),
|
||||
testing::ElementsAre(testing::AllOf(
|
||||
IsDiagnosticLabel(Diagnostics::LabelCategory::Primary,
|
||||
range.line_number, range.column_number, ""),
|
||||
testing::Field("loc", &Diagnostics::Label::loc,
|
||||
testing::Field("length", &Diagnostics::Loc::length,
|
||||
range.length)))));
|
||||
}
|
||||
|
||||
inline auto IsSingleDiagnostic(testing::Matcher<Diagnostics::Kind> kind,
|
||||
testing::Matcher<Diagnostics::Level> level,
|
||||
testing::Matcher<int> line_number,
|
||||
@@ -110,6 +143,24 @@ inline auto IsSingleDiagnostic(testing::Matcher<Diagnostics::Kind> kind,
|
||||
testing::IsEmpty(), testing::IsEmpty());
|
||||
}
|
||||
|
||||
// Matches a diagnostic with one message, located at the point just before
|
||||
// `column_number`, and nothing attached to it.
|
||||
inline auto IsPointDiagnostic(testing::Matcher<Diagnostics::Kind> kind,
|
||||
testing::Matcher<Diagnostics::Level> level,
|
||||
testing::Matcher<int> line_number,
|
||||
testing::Matcher<int> column_number,
|
||||
testing::Matcher<std::string> message)
|
||||
-> testing::Matcher<Diagnostics::Diagnostic> {
|
||||
return IsDiagnostic(
|
||||
level,
|
||||
testing::AllOf(
|
||||
IsDiagnosticMessage(kind, level, line_number, column_number, message),
|
||||
testing::Field(
|
||||
"loc", &Diagnostics::Message::loc,
|
||||
testing::Field("length", &Diagnostics::Loc::length, 0))),
|
||||
testing::IsEmpty(), testing::IsEmpty());
|
||||
}
|
||||
|
||||
} // namespace Carbon::Testing
|
||||
|
||||
namespace Carbon::Diagnostics {
|
||||
|
||||
+24
-25
@@ -14,46 +14,45 @@
|
||||
// A tour of how a missing token is drawn, which is the case where nothing in
|
||||
// the source is wrong: what the reader needs is not a range to look at but a
|
||||
// place to put something. See /toolchain/docs/diagnostics_rendering.md for
|
||||
// the rendering.
|
||||
// the rendering and /toolchain/docs/diagnostics.md for the convention.
|
||||
//
|
||||
// The rule is that the message is located at the token the parser found where
|
||||
// the missing one should be, which is the first token after the gap. That token
|
||||
// is underlined like any other range, so the place to put the missing token is
|
||||
// its left edge. When it is the end of the file, the message is located just
|
||||
// past the last token, which covers no text and is drawn as a lone `┳`.
|
||||
// The rule is that the message is located at the point where the missing
|
||||
// token would be inserted, which covers no text and is drawn as a lone `┳`. A
|
||||
// token that goes after another is inserted just past it, even when the next
|
||||
// token is on a later line or is the end of the file. An opening delimiter goes
|
||||
// against the token after it when that token is on the same line.
|
||||
//
|
||||
// This is a parse-phase tour, so nothing here reaches checking. What each line
|
||||
// is for:
|
||||
//
|
||||
// - `fn Paint`: a separator missing between two elements of a list, located at
|
||||
// the first token of the element after the gap.
|
||||
// - `if true`: an opening delimiter missing after its introducer, located at
|
||||
// the token after the introducer.
|
||||
// - `fn Paint`: a separator missing between two elements of a list, located
|
||||
// just past the element it goes after.
|
||||
// - `if true`: an opening delimiter missing after its introducer, located just
|
||||
// before the token it goes against.
|
||||
// - `var n`: the lexer inserting a `)` to recover, which the label reports,
|
||||
// because everything reported after that point is about that reading of the
|
||||
// file rather than about what was written.
|
||||
// - `class Widget`: a `;` missing at the end of a declaration, which is the
|
||||
// case where the next token is the end of the file, so the message is drawn
|
||||
// as a point just past `Widget`. This is last in the file because recovery
|
||||
// skips to what looks like the end of the declaration, which consumes
|
||||
// everything after it.
|
||||
// - `class Widget`: a `;` missing at the end of a declaration, located just
|
||||
// past the last token of the declaration. This is last in the file because
|
||||
// recovery skips to what looks like the end of the declaration, which
|
||||
// consumes everything after it. It is also the case where the next token is
|
||||
// the end of the file.
|
||||
|
||||
library "recovery_demo";
|
||||
|
||||
// CHECK:STDERR: ╭─┤ fail_recovery_demo.carbon:[[@LINE+7]]:17
|
||||
// CHECK:STDERR: -> 50 │ fn Paint(a: i32 b: i32);
|
||||
// CHECK:STDERR: · ┳
|
||||
// CHECK:STDERR: · ┗━━┫ error: expected `,` or `)`
|
||||
// CHECK:STDERR: · ┃ [UnexpectedTokenAfterListElement]
|
||||
// CHECK:STDERR: ╭─┤ fail_recovery_demo.carbon:[[@LINE+6]]:16
|
||||
// CHECK:STDERR: -> 49 │ fn Paint(a: i32 b: i32);
|
||||
// CHECK:STDERR: · ┳
|
||||
// CHECK:STDERR: · ┗━━┫ error: expected `,` or `)` [UnexpectedTokenAfterListElement]
|
||||
// CHECK:STDERR: ╰────
|
||||
// CHECK:STDERR:
|
||||
fn Paint(a: i32 b: i32);
|
||||
|
||||
fn Blend() {
|
||||
// CHECK:STDERR: ╭─┤ fail_recovery_demo.carbon:[[@LINE+6]]:6
|
||||
// CHECK:STDERR: -> 59 │ if true { }
|
||||
// CHECK:STDERR: · ━━┳━
|
||||
// CHECK:STDERR: · ┗━━┫ error: expected `(` after `if` [ExpectedParenAfter]
|
||||
// CHECK:STDERR: -> 58 │ if true { }
|
||||
// CHECK:STDERR: · ┳
|
||||
// CHECK:STDERR: · ┗━━┫ error: expected `(` after `if` [ExpectedParenAfter]
|
||||
// CHECK:STDERR: ╰────
|
||||
// CHECK:STDERR:
|
||||
if true { }
|
||||
@@ -61,7 +60,7 @@ fn Blend() {
|
||||
|
||||
fn Tint() {
|
||||
// CHECK:STDERR: ╭─┤ fail_recovery_demo.carbon:[[@LINE+9]]:16
|
||||
// CHECK:STDERR: -> 72 │ var n: i32 = (1;
|
||||
// CHECK:STDERR: -> 71 │ var n: i32 = (1;
|
||||
// CHECK:STDERR: · ┳ ┬
|
||||
// CHECK:STDERR: · ┗━━━━┫ error: opening symbol without a corresponding closing
|
||||
// CHECK:STDERR: · │ ┃ symbol
|
||||
@@ -73,7 +72,7 @@ fn Tint() {
|
||||
}
|
||||
|
||||
// CHECK:STDERR: ╭─┤ fail_recovery_demo.carbon:[[@LINE+8]]:13
|
||||
// CHECK:STDERR: -> 83 │ class Widget
|
||||
// CHECK:STDERR: -> 82 │ class Widget
|
||||
// CHECK:STDERR: · ┳
|
||||
// CHECK:STDERR: · ┗━━┫ error: `class` declarations must either end with a `;` or have
|
||||
// CHECK:STDERR: · ┃ a `{ ... }` block for a definition
|
||||
|
||||
@@ -20,6 +20,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
- [Diagnostic registry](#diagnostic-registry)
|
||||
- [CARBON_DIAGNOSTIC placement](#carbon_diagnostic-placement)
|
||||
- [Choosing what a label marks](#choosing-what-a-label-marks)
|
||||
- [Locating lexer and parser diagnostics](#locating-lexer-and-parser-diagnostics)
|
||||
- [Diagnostic parameter types](#diagnostic-parameter-types)
|
||||
- [Diagnostic message style guide](#diagnostic-message-style-guide)
|
||||
- [Alternatives considered](#alternatives-considered)
|
||||
@@ -378,6 +379,47 @@ Until a diagnostic is migrated its message location supplies the range, which is
|
||||
usually the one column an editor would put a cursor on and rarely an extent
|
||||
worth looking at.
|
||||
|
||||
## Locating lexer and parser diagnostics
|
||||
|
||||
A lexer or parser diagnostic is about text the developer is about to edit, so
|
||||
its message is located where they would put the cursor to start fixing it:
|
||||
|
||||
- Something missing is located at the point where it would be inserted. A
|
||||
point covers no text: it is a `Diagnostics::Loc` with a `length` of zero. A
|
||||
missing token is inserted just past the token before it, so a missing `;`
|
||||
at the end of a line is located at the end of that line rather than at the
|
||||
first token of the next. An opening bracket is inserted against the token
|
||||
after it when that token is on the same line, so `expected braced code
|
||||
block` in `if (x) return;` is located just before `return`.
|
||||
|
||||
- Something present but wrong is located at the tokens it covers. A word
|
||||
that can't be a name, such as a keyword, in a name's position and followed
|
||||
by a token that can follow the name, is taken as the name: the message
|
||||
covers it and suggests `r#`.
|
||||
|
||||
- Something missing at the start of a construct is located at the token
|
||||
found in its place. The token before the point belongs to another
|
||||
construct, possibly on another line, so an expression statement that starts
|
||||
with `=` is reported at the `=` rather than at the end of the statement
|
||||
before it.
|
||||
|
||||
- Inside a token, the lexer locates the message at the point to fix and
|
||||
attaches the range of what is malformed, which the snippet underlines
|
||||
instead. An unterminated string literal is located where its terminator
|
||||
goes and underlines the literal; a `\x` escape without two digits is
|
||||
located at the digit that is missing or wrong and underlines the escape.
|
||||
|
||||
In the lexer, a location is a `Lex::SourceLoc`. A `const char*` into the source
|
||||
buffer converts to the one character it points at, `SourceLoc::Before` makes a
|
||||
point, and `SourceLoc::Range` makes any other range. A range is never made from
|
||||
a bare `const char*`, because converting one to `llvm::StringRef` scans for a
|
||||
null byte, and a source buffer isn't guaranteed to have one.
|
||||
|
||||
In the parser, a location is a `Parse::DiagnosticLoc`. A token converts to one,
|
||||
`DiagnosticLoc::Tokens` covers a run of tokens, and `Before` and `After` make
|
||||
the points on either side of a token. `Context::PointAfterPreviousToken` and
|
||||
`Context::OpeningBracketInsertionPoint` locate most missing tokens.
|
||||
|
||||
## Diagnostic parameter types
|
||||
|
||||
Diagnostic parameters should have informative types. We rely on three different
|
||||
|
||||
+4
-4
@@ -78,12 +78,12 @@ fn G() {}
|
||||
// CHECK:STDOUT: "message": "`fn` declarations must either end with a `;` or have a `{ ... }` block for a definition",
|
||||
// CHECK:STDOUT: "range": {
|
||||
// CHECK:STDOUT: "end": {
|
||||
// CHECK:STDOUT: "character": 2,
|
||||
// CHECK:STDOUT: "line": 2
|
||||
// CHECK:STDOUT: "character": 4,
|
||||
// CHECK:STDOUT: "line": 0
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "start": {
|
||||
// CHECK:STDOUT: "character": 0,
|
||||
// CHECK:STDOUT: "line": 2
|
||||
// CHECK:STDOUT: "character": 4,
|
||||
// CHECK:STDOUT: "line": 0
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: },
|
||||
// CHECK:STDOUT: "severity": 1,
|
||||
|
||||
@@ -47,6 +47,7 @@ cc_library(
|
||||
srcs = ["helpers.cpp"],
|
||||
hdrs = ["helpers.h"],
|
||||
deps = [
|
||||
":source_loc",
|
||||
"//toolchain/diagnostics:emitter",
|
||||
"@llvm-project//llvm:Support",
|
||||
],
|
||||
@@ -57,6 +58,7 @@ cc_library(
|
||||
testonly = 1,
|
||||
hdrs = ["test_helpers.h"],
|
||||
deps = [
|
||||
":source_loc",
|
||||
"//common:check",
|
||||
"//common:string_helpers",
|
||||
"//toolchain/diagnostics:emitter",
|
||||
@@ -72,6 +74,7 @@ cc_library(
|
||||
deps = [
|
||||
":character_set",
|
||||
":helpers",
|
||||
":source_loc",
|
||||
"//common:check",
|
||||
"//toolchain/base:int",
|
||||
"//toolchain/diagnostics:emitter",
|
||||
@@ -131,6 +134,7 @@ cc_library(
|
||||
deps = [
|
||||
":character_set",
|
||||
":helpers",
|
||||
":source_loc",
|
||||
":token_info",
|
||||
"//common:check",
|
||||
"//toolchain/diagnostics:emitter",
|
||||
@@ -191,6 +195,7 @@ cc_library(
|
||||
":helpers",
|
||||
":mismatched_brackets",
|
||||
":numeric_literal",
|
||||
":source_loc",
|
||||
":string_literal",
|
||||
":token_index",
|
||||
":token_info",
|
||||
@@ -308,6 +313,15 @@ cc_library(
|
||||
alwayslink = 1,
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "source_loc",
|
||||
hdrs = ["source_loc.h"],
|
||||
deps = [
|
||||
"//common:check",
|
||||
"@llvm-project//llvm:Support",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "token_index",
|
||||
hdrs = ["token_index.h"],
|
||||
@@ -337,6 +351,7 @@ cc_library(
|
||||
":character_set",
|
||||
":helpers",
|
||||
":numeric_literal",
|
||||
":source_loc",
|
||||
":string_literal",
|
||||
":token_index",
|
||||
":token_info",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
namespace Carbon::Lex {
|
||||
|
||||
auto CanLexInt(Diagnostics::Emitter<const char*>& emitter, llvm::StringRef text)
|
||||
auto CanLexInt(Diagnostics::Emitter<SourceLoc>& emitter, llvm::StringRef text)
|
||||
-> bool {
|
||||
// Integer parsing has poor scaling characteristics for extremely large digit
|
||||
// amounts. We've done some performance work on this, but this limit exists to
|
||||
@@ -21,7 +21,9 @@ auto CanLexInt(Diagnostics::Emitter<const char*>& emitter, llvm::StringRef text)
|
||||
"found a sequence of {0} digits, which is greater than the "
|
||||
"limit of {1}",
|
||||
size_t, size_t);
|
||||
emitter.Emit(text.begin(), TooManyDigits, text.size(), DigitLimit);
|
||||
emitter.Build(text.begin(), TooManyDigits, text.size(), DigitLimit)
|
||||
.Attach(SourceLoc::Range(text))
|
||||
.Emit();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -6,12 +6,13 @@
|
||||
#define CARBON_TOOLCHAIN_LEX_HELPERS_H_
|
||||
|
||||
#include "toolchain/diagnostics/emitter.h"
|
||||
#include "toolchain/lex/source_loc.h"
|
||||
|
||||
namespace Carbon::Lex {
|
||||
|
||||
// Should guard calls to getAsInteger due to performance issues with large
|
||||
// integers. Emits an error if the text cannot be lexed.
|
||||
auto CanLexInt(Diagnostics::Emitter<const char*>& emitter, llvm::StringRef text)
|
||||
auto CanLexInt(Diagnostics::Emitter<SourceLoc>& emitter, llvm::StringRef text)
|
||||
-> bool;
|
||||
|
||||
} // namespace Carbon::Lex
|
||||
|
||||
+44
-22
@@ -24,6 +24,7 @@
|
||||
#include "toolchain/lex/helpers.h"
|
||||
#include "toolchain/lex/mismatched_brackets.h"
|
||||
#include "toolchain/lex/numeric_literal.h"
|
||||
#include "toolchain/lex/source_loc.h"
|
||||
#include "toolchain/lex/string_literal.h"
|
||||
#include "toolchain/lex/token_index.h"
|
||||
#include "toolchain/lex/token_info.h"
|
||||
@@ -259,7 +260,7 @@ class [[clang::internal_linkage]] Lexer {
|
||||
|
||||
Diagnostics::ErrorTrackingConsumer consumer_;
|
||||
|
||||
TokenizedBuffer::SourcePointerDiagnosticEmitter emitter_;
|
||||
TokenizedBuffer::SourceLocDiagnosticEmitter emitter_;
|
||||
|
||||
TokenizedBuffer::TokenDiagnosticEmitter token_emitter_;
|
||||
};
|
||||
@@ -1254,7 +1255,8 @@ auto Lexer::LexComment(llvm::StringRef source_text, ssize_t& position) -> void {
|
||||
}
|
||||
CARBON_DIAGNOSTIC(NoWhitespaceAfterCommentIntroducer, Error,
|
||||
"whitespace is required after '//'");
|
||||
emitter_.Emit(comment_text.begin() + 2, NoWhitespaceAfterCommentIntroducer);
|
||||
emitter_.Emit(SourceLoc::Before(comment_text.begin() + 2),
|
||||
NoWhitespaceAfterCommentIntroducer);
|
||||
|
||||
// We use this to tweak the lexing of blocks below.
|
||||
is_valid_after_slashes = false;
|
||||
@@ -1458,13 +1460,18 @@ auto Lexer::LexNumericLiteral(llvm::StringRef source_text, ssize_t& position)
|
||||
}
|
||||
}
|
||||
|
||||
static auto DiagnoseUnterminatedString(
|
||||
Diagnostics::Emitter<const char*>& emitter, const StringLiteral& literal,
|
||||
bool is_char) -> void {
|
||||
static auto DiagnoseUnterminatedString(Diagnostics::Emitter<SourceLoc>& emitter,
|
||||
const StringLiteral& literal,
|
||||
bool is_char) -> void {
|
||||
CARBON_DIAGNOSTIC(UnterminatedString, Error,
|
||||
"{0:character|string} literal is missing a terminator",
|
||||
Diagnostics::BoolAsSelect);
|
||||
emitter.Emit(literal.text().begin(), UnterminatedString, is_char);
|
||||
// The terminator goes where the literal stops.
|
||||
emitter
|
||||
.Build(SourceLoc::Before(literal.text().end()), UnterminatedString,
|
||||
is_char)
|
||||
.Attach(SourceLoc::Range(literal.text()))
|
||||
.Emit();
|
||||
}
|
||||
|
||||
auto Lexer::LexStringLiteral(llvm::StringRef source_text, ssize_t& position)
|
||||
@@ -1504,7 +1511,9 @@ auto Lexer::LexStringLiteral(llvm::StringRef source_text, ssize_t& position)
|
||||
"invalid multi-line string literal introducer; a file "
|
||||
"type indicator may not contain `'`, `#`, or `\"`, and "
|
||||
"the content must begin on a new line");
|
||||
emitter_.Emit(literal->text().begin(), MultiLineStringInvalidIntroducer);
|
||||
emitter_.Build(literal->text().begin(), MultiLineStringInvalidIntroducer)
|
||||
.Attach(SourceLoc::Range(literal->text()))
|
||||
.Emit();
|
||||
return lex_as_error();
|
||||
}
|
||||
|
||||
@@ -1533,6 +1542,10 @@ auto Lexer::LexStringLiteral(llvm::StringRef source_text, ssize_t& position)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Recover from an unterminated literal in a way that lets parsing
|
||||
// continue as if it were terminated. The error token makes the parser also
|
||||
// report the enclosing construct as incomplete, for example a missing `;`
|
||||
// after `var s: str = "abc`.
|
||||
if (!literal->is_terminated()) {
|
||||
DiagnoseUnterminatedString(emitter_, *literal, /*is_char=*/false);
|
||||
return lex_as_error();
|
||||
@@ -1659,8 +1672,11 @@ auto Lexer::LexWordAsTypeLiteralToken(llvm::StringRef word, int32_t byte_offset)
|
||||
"found a type literal with a bit width using {0} digits, "
|
||||
"which is greater than the limit of {1}",
|
||||
size_t, size_t);
|
||||
emitter_.Emit(word.begin() + 1, TooManyTypeBitWidthDigits, suffix.size(),
|
||||
DigitLimit);
|
||||
emitter_
|
||||
.Build(word.begin() + 1, TooManyTypeBitWidthDigits, suffix.size(),
|
||||
DigitLimit)
|
||||
.Attach(SourceLoc::Range(suffix))
|
||||
.Emit();
|
||||
return LexTokenWithPayload(TokenKind::Error, word.size(), byte_offset);
|
||||
}
|
||||
|
||||
@@ -1720,7 +1736,7 @@ auto Lexer::LexWordAsDollarIntLiteralToken(llvm::StringRef word,
|
||||
if (word.size() < 2) {
|
||||
CARBON_DIAGNOSTIC(DollarIntLiteralMissingNumber, Error,
|
||||
"expected digits after `$`");
|
||||
emitter_.Emit(word.begin() + 1, DollarIntLiteralMissingNumber);
|
||||
emitter_.Emit(SourceLoc::Before(word.end()), DollarIntLiteralMissingNumber);
|
||||
return LexTokenWithPayload(TokenKind::Error, word.size(), byte_offset);
|
||||
}
|
||||
if (word[1] == '0' && word.size() > 2) {
|
||||
@@ -1866,7 +1882,9 @@ auto Lexer::LexError(llvm::StringRef source_text, ssize_t& position)
|
||||
LexTokenWithPayload(TokenKind::Error, error_text.size(), position);
|
||||
CARBON_DIAGNOSTIC(UnrecognizedCharacters, Error,
|
||||
"encountered unrecognized characters while parsing");
|
||||
emitter_.Emit(error_text.begin(), UnrecognizedCharacters);
|
||||
emitter_.Build(error_text.begin(), UnrecognizedCharacters)
|
||||
.Attach(SourceLoc::Range(error_text))
|
||||
.Emit();
|
||||
|
||||
position += error_text.size();
|
||||
return token;
|
||||
@@ -2383,12 +2401,12 @@ static auto TokenEndPosition(const TokenizedBuffer& buffer, TokenIndex token)
|
||||
return TokenStartPosition(buffer, token) + buffer.GetTokenText(token).size();
|
||||
}
|
||||
|
||||
// Where in the source a bracket that `correction` proposes would be written:
|
||||
// just past the end of the token it goes after, rather than at the start of the
|
||||
// token it goes before. For `f(x` on one line and `;` on the next, that points
|
||||
// the suggestion at the position directly after the `x`, where the `)` belongs,
|
||||
// instead of down at the `;`. Where the two tokens are separated, the bracket
|
||||
// is then placed on the side that matches how it would be written.
|
||||
// Where a user would insert the bracket that `correction` proposes: just past
|
||||
// the end of the token it goes after, rather than at the start of the token it
|
||||
// goes before. For `f(x` on one line and `;` on the next, that points the
|
||||
// suggestion at the position directly after the `x`, where the `)` belongs,
|
||||
// instead of down at the `;`. An opening bracket and a `}` closing a multi-line
|
||||
// scope are inserted elsewhere, as described below.
|
||||
static auto BracketInsertionPosition(const TokenizedBuffer& buffer,
|
||||
const BracketCorrection& correction)
|
||||
-> const char* {
|
||||
@@ -2427,9 +2445,12 @@ static auto BracketInsertionPosition(const TokenizedBuffer& buffer,
|
||||
buffer.GetIndentColumnNumber(open_line) - 1);
|
||||
}
|
||||
} else if (correction.fix_token_kind.is_opening_symbol()) {
|
||||
// An opening bracket binds to what comes after it, so it belongs on the far
|
||||
// side of any space separating it from the token it follows.
|
||||
pos = skip_spaces(pos, NoLimit);
|
||||
// An opening bracket is inserted against the token after it when that token
|
||||
// is on the same line.
|
||||
TokenIndex insert_before(insert_after.index + 1);
|
||||
if (buffer.GetEndLoc(insert_after).first == buffer.GetLine(insert_before)) {
|
||||
pos = TokenStartPosition(buffer, insert_before);
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
@@ -2470,8 +2491,9 @@ auto Lexer::DiagnoseAndFixMismatchedBrackets() -> void {
|
||||
// on the bracket rather than suggest one of them.
|
||||
fixes.ReplaceWithError(correction.diagnostic_token_index);
|
||||
} else {
|
||||
builder.Attach(BracketInsertionPosition(buffer_, correction),
|
||||
PossiblyMissingBracketHere, correction.fix_token_kind);
|
||||
builder.Attach(
|
||||
SourceLoc::Before(BracketInsertionPosition(buffer_, correction)),
|
||||
PossiblyMissingBracketHere, correction.fix_token_kind);
|
||||
insertion_ids[correction_index] =
|
||||
correction.fix_action == BracketFixAction::InsertBefore
|
||||
? fixes.InsertBefore(correction.fix_token_index,
|
||||
|
||||
@@ -93,7 +93,7 @@ auto NumericLiteral::Lex(llvm::StringRef source_text,
|
||||
// either diagnosing or extracting its meaning.
|
||||
class NumericLiteral::Parser {
|
||||
public:
|
||||
Parser(Diagnostics::Emitter<const char*>& emitter, NumericLiteral literal);
|
||||
Parser(Diagnostics::Emitter<SourceLoc>& emitter, NumericLiteral literal);
|
||||
|
||||
auto IsInt() -> bool {
|
||||
return literal_.radix_point_ == static_cast<int>(literal_.text_.size());
|
||||
@@ -129,7 +129,7 @@ class NumericLiteral::Parser {
|
||||
auto CheckFractionalPart() -> bool;
|
||||
auto CheckExponentPart() -> bool;
|
||||
|
||||
Diagnostics::Emitter<const char*>& emitter_;
|
||||
Diagnostics::Emitter<SourceLoc>& emitter_;
|
||||
NumericLiteral literal_;
|
||||
|
||||
// The radix of the literal: 2, 10, or 16, for a prefix of '0b', no prefix,
|
||||
@@ -152,7 +152,7 @@ class NumericLiteral::Parser {
|
||||
bool exponent_is_negative_ = false;
|
||||
};
|
||||
|
||||
NumericLiteral::Parser::Parser(Diagnostics::Emitter<const char*>& emitter,
|
||||
NumericLiteral::Parser::Parser(Diagnostics::Emitter<SourceLoc>& emitter,
|
||||
NumericLiteral literal)
|
||||
: emitter_(emitter), literal_(literal) {
|
||||
int_part_ = literal.text_.substr(0, literal.radix_point_);
|
||||
@@ -406,7 +406,9 @@ auto NumericLiteral::Parser::CheckDigitSequence(llvm::StringRef text,
|
||||
if (num_digit_separators == static_cast<int>(text.size())) {
|
||||
CARBON_DIAGNOSTIC(EmptyDigitSequence, Error,
|
||||
"empty digit sequence in numeric literal");
|
||||
emitter_.Emit(text.begin(), EmptyDigitSequence);
|
||||
emitter_.Build(SourceLoc::Before(text.begin()), EmptyDigitSequence)
|
||||
.Attach(SourceLoc::Range(text))
|
||||
.Emit();
|
||||
return {.ok = false};
|
||||
}
|
||||
|
||||
@@ -423,7 +425,9 @@ auto NumericLiteral::Parser::CheckLeadingZero() -> bool {
|
||||
int_part_ != "0") {
|
||||
CARBON_DIAGNOSTIC(UnknownBaseSpecifier, Error,
|
||||
"unknown base specifier in numeric literal");
|
||||
emitter_.Emit(int_part_.begin(), UnknownBaseSpecifier);
|
||||
emitter_.Build(int_part_.begin(), UnknownBaseSpecifier)
|
||||
.Attach(SourceLoc::Range(int_part_))
|
||||
.Emit();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -448,8 +452,11 @@ auto NumericLiteral::Parser::CheckFractionalPart() -> bool {
|
||||
InvalidRealLiteralRadix, Error,
|
||||
"{0:=2:binary|=8:octal} real number literals are not supported",
|
||||
Diagnostics::IntAsSelect);
|
||||
emitter_.Emit(literal_.text_.begin() + literal_.radix_point_,
|
||||
InvalidRealLiteralRadix, static_cast<int>(radix_));
|
||||
emitter_
|
||||
.Build(literal_.text_.begin() + literal_.radix_point_,
|
||||
InvalidRealLiteralRadix, static_cast<int>(radix_))
|
||||
.Attach(SourceLoc::Range(literal_.text_))
|
||||
.Emit();
|
||||
// Carry on and parse the real literal anyway.
|
||||
}
|
||||
|
||||
@@ -483,7 +490,7 @@ auto NumericLiteral::Parser::CheckExponentPart() -> bool {
|
||||
|
||||
// Parse the token and compute its value.
|
||||
auto NumericLiteral::ComputeValue(
|
||||
Diagnostics::Emitter<const char*>& emitter) const -> Value {
|
||||
Diagnostics::Emitter<SourceLoc>& emitter) const -> Value {
|
||||
Parser parser(emitter, *this);
|
||||
|
||||
if (!parser.Check()) {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "llvm/ADT/APInt.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "toolchain/diagnostics/emitter.h"
|
||||
#include "toolchain/lex/source_loc.h"
|
||||
|
||||
namespace Carbon::Lex {
|
||||
|
||||
@@ -52,7 +53,7 @@ class NumericLiteral {
|
||||
|
||||
// Compute the value of the token, if possible. Emit diagnostics to the given
|
||||
// emitter if the token is not valid.
|
||||
auto ComputeValue(Diagnostics::Emitter<const char*>& emitter) const -> Value;
|
||||
auto ComputeValue(Diagnostics::Emitter<SourceLoc>& emitter) const -> Value;
|
||||
|
||||
// Get the text corresponding to this literal.
|
||||
auto text() const -> llvm::StringRef { return text_; }
|
||||
|
||||
@@ -52,7 +52,7 @@ static void BM_Lex_IntDecimalN(benchmark::State& state) {
|
||||
static void BM_ComputeValue_Float(benchmark::State& state) {
|
||||
auto val = NumericLiteral::Lex("0.000001", true);
|
||||
CARBON_CHECK(val);
|
||||
auto& emitter = Diagnostics::NullEmitter<const char*>();
|
||||
auto& emitter = Diagnostics::NullEmitter<Lex::SourceLoc>();
|
||||
for (auto _ : state) {
|
||||
val->ComputeValue(emitter);
|
||||
}
|
||||
@@ -60,7 +60,7 @@ static void BM_ComputeValue_Float(benchmark::State& state) {
|
||||
|
||||
static void BM_ComputeValue_Int(benchmark::State& state) {
|
||||
auto val = NumericLiteral::Lex("1_234_567_890", true);
|
||||
auto& emitter = Diagnostics::NullEmitter<const char*>();
|
||||
auto& emitter = Diagnostics::NullEmitter<Lex::SourceLoc>();
|
||||
CARBON_CHECK(val);
|
||||
for (auto _ : state) {
|
||||
val->ComputeValue(emitter);
|
||||
@@ -70,7 +70,7 @@ static void BM_ComputeValue_Int(benchmark::State& state) {
|
||||
static void BM_ComputeValue_IntDecimalN(benchmark::State& state) {
|
||||
std::string s = MakeIntString("", 10, state.range(0));
|
||||
auto val = NumericLiteral::Lex(s, true);
|
||||
auto& emitter = Diagnostics::NullEmitter<const char*>();
|
||||
auto& emitter = Diagnostics::NullEmitter<Lex::SourceLoc>();
|
||||
CARBON_CHECK(val);
|
||||
for (auto _ : state) {
|
||||
val->ComputeValue(emitter);
|
||||
@@ -80,7 +80,7 @@ static void BM_ComputeValue_IntDecimalN(benchmark::State& state) {
|
||||
static void BM_ComputeValue_IntBinaryN(benchmark::State& state) {
|
||||
std::string s = MakeIntString("0b", 2, state.range(0));
|
||||
auto val = NumericLiteral::Lex(s, true);
|
||||
auto& emitter = Diagnostics::NullEmitter<const char*>();
|
||||
auto& emitter = Diagnostics::NullEmitter<Lex::SourceLoc>();
|
||||
CARBON_CHECK(val);
|
||||
for (auto _ : state) {
|
||||
val->ComputeValue(emitter);
|
||||
@@ -90,7 +90,7 @@ static void BM_ComputeValue_IntBinaryN(benchmark::State& state) {
|
||||
static void BM_ComputeValue_IntOctalN(benchmark::State& state) {
|
||||
std::string s = MakeIntString("0o", 8, state.range(0));
|
||||
auto val = NumericLiteral::Lex(s, true);
|
||||
auto& emitter = Diagnostics::NullEmitter<const char*>();
|
||||
auto& emitter = Diagnostics::NullEmitter<Lex::SourceLoc>();
|
||||
CARBON_CHECK(val);
|
||||
for (auto _ : state) {
|
||||
val->ComputeValue(emitter);
|
||||
@@ -101,7 +101,7 @@ static void BM_ComputeValue_IntHexN(benchmark::State& state) {
|
||||
// 0 is in the middle so that it isn't truncated in parse.
|
||||
std::string s = MakeIntString("0x", 16, state.range(0));
|
||||
auto val = NumericLiteral::Lex(s, true);
|
||||
auto& emitter = Diagnostics::NullEmitter<const char*>();
|
||||
auto& emitter = Diagnostics::NullEmitter<Lex::SourceLoc>();
|
||||
CARBON_CHECK(val);
|
||||
for (auto _ : state) {
|
||||
val->ComputeValue(emitter);
|
||||
|
||||
@@ -21,7 +21,7 @@ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
|
||||
}
|
||||
|
||||
volatile auto value =
|
||||
token->ComputeValue(Diagnostics::NullEmitter<const char*>());
|
||||
token->ComputeValue(Diagnostics::NullEmitter<Lex::SourceLoc>());
|
||||
(void)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#ifndef CARBON_TOOLCHAIN_LEX_SOURCE_LOC_H_
|
||||
#define CARBON_TOOLCHAIN_LEX_SOURCE_LOC_H_
|
||||
|
||||
#include "common/check.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
namespace Carbon::Lex {
|
||||
|
||||
// The location of a lexer diagnostic: a range of the source buffer. An empty
|
||||
// range names the point between two characters, where something missing would
|
||||
// be written.
|
||||
//
|
||||
// A `const char*` into the source buffer converts implicitly to the one
|
||||
// character it points at, and any other range has to be made by a factory
|
||||
// function. Converting a bare `const char*` to `llvm::StringRef` scans for a
|
||||
// null byte, and a source buffer isn't guaranteed to have one.
|
||||
class SourceLoc {
|
||||
public:
|
||||
// The character at `pos`, which must point into the source buffer.
|
||||
//
|
||||
// NOLINTNEXTLINE(google-explicit-constructor): A character is a location.
|
||||
SourceLoc(const char* pos) : text_(pos, 1) {}
|
||||
|
||||
// The range `text` covers, which must point into the source buffer.
|
||||
static auto Range(llvm::StringRef text) -> SourceLoc {
|
||||
return SourceLoc(text);
|
||||
}
|
||||
|
||||
// The range from `begin` up to `end`, both of which must point into the
|
||||
// source buffer.
|
||||
static auto Range(const char* begin, const char* end) -> SourceLoc {
|
||||
CARBON_CHECK(begin <= end, "A range's end can't come before its start.");
|
||||
return SourceLoc(llvm::StringRef(begin, end - begin));
|
||||
}
|
||||
|
||||
// The point just before `pos`, which must point into the source buffer or
|
||||
// just past its end.
|
||||
static auto Before(const char* pos) -> SourceLoc { return Range(pos, pos); }
|
||||
|
||||
// The source text this location covers.
|
||||
auto text() const -> llvm::StringRef { return text_; }
|
||||
|
||||
private:
|
||||
explicit SourceLoc(llvm::StringRef text) : text_(text) {}
|
||||
|
||||
llvm::StringRef text_;
|
||||
};
|
||||
|
||||
} // namespace Carbon::Lex
|
||||
|
||||
#endif // CARBON_TOOLCHAIN_LEX_SOURCE_LOC_H_
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
namespace Carbon::Lex {
|
||||
|
||||
using DiagnosticEmitter = Diagnostics::Emitter<const char*>;
|
||||
using DiagnosticEmitter = Diagnostics::Emitter<SourceLoc>;
|
||||
|
||||
static constexpr char MultiLineIndicator[] = R"(''')";
|
||||
static constexpr char DoubleQuotedMultiLineIndicator[] = R"(""")";
|
||||
@@ -284,7 +284,9 @@ static auto CheckIndent(DiagnosticEmitter& emitter, llvm::StringRef text,
|
||||
ContentBeforeStringTerminator, Error,
|
||||
"only whitespace is permitted before the closing `'''` of a "
|
||||
"multi-line string");
|
||||
emitter.Emit(indent.end(), ContentBeforeStringTerminator);
|
||||
emitter.Build(indent.end(), ContentBeforeStringTerminator)
|
||||
.Attach(SourceLoc::Range(indent.end(), content.end()))
|
||||
.Emit();
|
||||
}
|
||||
|
||||
return indent;
|
||||
@@ -302,7 +304,9 @@ static auto ExpandUnicodeEscapeSequence(DiagnosticEmitter& emitter,
|
||||
CARBON_DIAGNOSTIC(UnicodeEscapeTooLarge, Error,
|
||||
"code point specified by `\\u{{...}}` escape is greater "
|
||||
"than 0x10FFFF");
|
||||
emitter.Emit(digits.begin(), UnicodeEscapeTooLarge);
|
||||
emitter.Build(digits.begin(), UnicodeEscapeTooLarge)
|
||||
.Attach(SourceLoc::Range(digits))
|
||||
.Emit();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -310,7 +314,9 @@ static auto ExpandUnicodeEscapeSequence(DiagnosticEmitter& emitter,
|
||||
CARBON_DIAGNOSTIC(UnicodeEscapeSurrogate, Error,
|
||||
"code point specified by `\\u{{...}}` escape is a "
|
||||
"surrogate character");
|
||||
emitter.Emit(digits.begin(), UnicodeEscapeSurrogate);
|
||||
emitter.Build(digits.begin(), UnicodeEscapeSurrogate)
|
||||
.Attach(SourceLoc::Range(digits))
|
||||
.Emit();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -348,10 +354,18 @@ static auto AppendFrontOfContents(char*& buffer_cursor,
|
||||
// `result` string. `content` is the string content, starting from the first
|
||||
// character after the escape sequence introducer (for example, the `n` in
|
||||
// `\n`), and will be updated to remove the leading escape sequence.
|
||||
// `escape_begin` points at the `\` that starts the introducer, which a raw
|
||||
// string follows with its `#`s.
|
||||
static auto ExpandAndConsumeEscapeSequence(DiagnosticEmitter& emitter,
|
||||
const char* escape_begin,
|
||||
llvm::StringRef& content,
|
||||
char*& buffer_cursor) -> void {
|
||||
CARBON_CHECK(!content.empty(), "should have escaped closing delimiter");
|
||||
// A diagnostic about the escape marks it from the `\` through as much of it
|
||||
// as was consumed when the problem was found.
|
||||
auto escape_through = [&](const char* end) {
|
||||
return SourceLoc::Range(escape_begin, end);
|
||||
};
|
||||
char first = content.front();
|
||||
content = content.drop_front(1);
|
||||
|
||||
@@ -381,11 +395,13 @@ static auto ExpandAndConsumeEscapeSequence(DiagnosticEmitter& emitter,
|
||||
DecimalEscapeSequence, Error,
|
||||
"decimal digit follows `\\0` escape sequence. Use `\\x00` instead "
|
||||
"of `\\0` if the next character is a digit");
|
||||
emitter.Emit(content.begin(), DecimalEscapeSequence);
|
||||
emitter.Build(escape_begin, DecimalEscapeSequence)
|
||||
.Attach(escape_through(content.begin() + 1))
|
||||
.Emit();
|
||||
return;
|
||||
}
|
||||
return;
|
||||
case 'x':
|
||||
case 'x': {
|
||||
if (content.size() >= 2 && IsUpperHexDigit(content[0]) &&
|
||||
IsUpperHexDigit(content[1])) {
|
||||
AppendChar(buffer_cursor, static_cast<char>(llvm::hexFromNibbles(
|
||||
@@ -396,8 +412,16 @@ static auto ExpandAndConsumeEscapeSequence(DiagnosticEmitter& emitter,
|
||||
CARBON_DIAGNOSTIC(HexadecimalEscapeMissingDigits, Error,
|
||||
"escape sequence `\\x` must be followed by two "
|
||||
"uppercase hexadecimal digits, for example `\\x0F`");
|
||||
emitter.Emit(content.begin(), HexadecimalEscapeMissingDigits);
|
||||
// The digit that is missing or wrong is where the escape needs fixing.
|
||||
const char* bad_digit =
|
||||
content.begin() +
|
||||
content.take_front(2).take_while(IsUpperHexDigit).size();
|
||||
emitter
|
||||
.Build(SourceLoc::Before(bad_digit), HexadecimalEscapeMissingDigits)
|
||||
.Attach(escape_through(bad_digit))
|
||||
.Emit();
|
||||
break;
|
||||
}
|
||||
case 'u': {
|
||||
llvm::StringRef remaining = content;
|
||||
if (remaining.consume_front("{")) {
|
||||
@@ -415,13 +439,21 @@ static auto ExpandAndConsumeEscapeSequence(DiagnosticEmitter& emitter,
|
||||
UnicodeEscapeMissingBracedDigits, Error,
|
||||
"escape sequence `\\u` must be followed by a braced sequence of "
|
||||
"uppercase hexadecimal digits, for example `\\u{{70AD}}`");
|
||||
emitter.Emit(content.begin(), UnicodeEscapeMissingBracedDigits);
|
||||
// `remaining` starts at the part of `{...}` that is missing or wrong,
|
||||
// which is where the escape needs fixing.
|
||||
emitter
|
||||
.Build(SourceLoc::Before(remaining.begin()),
|
||||
UnicodeEscapeMissingBracedDigits)
|
||||
.Attach(escape_through(remaining.begin()))
|
||||
.Emit();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
CARBON_DIAGNOSTIC(UnknownEscapeSequence, Error,
|
||||
"unrecognized escape sequence `{0}`", char);
|
||||
emitter.Emit(content.begin() - 1, UnknownEscapeSequence, first);
|
||||
emitter.Build(escape_begin, UnknownEscapeSequence, first)
|
||||
.Attach(escape_through(content.begin()))
|
||||
.Emit();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -453,7 +485,14 @@ static auto ExpandEscapeSequencesAndRemoveIndent(
|
||||
MismatchedIndentInString, Error,
|
||||
"indentation does not match that of the closing `'''` in "
|
||||
"multi-line string literal");
|
||||
emitter.Emit(line_start, MismatchedIndentInString);
|
||||
CARBON_DIAGNOSTIC_LABEL(ClosingDelimiterIndent, Info,
|
||||
"closing `'''` is indented to here");
|
||||
auto diag = emitter.Build(line_start, MismatchedIndentInString);
|
||||
// A line with no leading whitespace has no indent to mark.
|
||||
if (contents.begin() != line_start) {
|
||||
diag.Attach(SourceLoc::Range(line_start, contents.begin()));
|
||||
}
|
||||
diag.Attach(SourceLoc::Range(indent), ClosingDelimiterIndent).Emit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,13 +539,13 @@ static auto ExpandEscapeSequencesAndRemoveIndent(
|
||||
auto after_space = contents.find_if_not(IsHorizontalWhitespace);
|
||||
if (after_space == llvm::StringRef::npos ||
|
||||
contents[after_space] != '\n') {
|
||||
// TODO: Include the source range of the whitespace up to
|
||||
// `contents.begin() + after_space` in the diagnostic.
|
||||
CARBON_DIAGNOSTIC(
|
||||
InvalidHorizontalWhitespaceInString, Error,
|
||||
"whitespace other than plain space must be expressed with an "
|
||||
"escape sequence in a string literal");
|
||||
emitter.Emit(contents.begin(), InvalidHorizontalWhitespaceInString);
|
||||
emitter.Build(contents.begin(), InvalidHorizontalWhitespaceInString)
|
||||
.Attach(SourceLoc::Range(contents.take_front(after_space)))
|
||||
.Emit();
|
||||
// Include the whitespace in the string contents for error recovery.
|
||||
AppendFrontOfContents(buffer_cursor, contents, after_space);
|
||||
}
|
||||
@@ -514,6 +553,7 @@ static auto ExpandEscapeSequencesAndRemoveIndent(
|
||||
continue;
|
||||
}
|
||||
|
||||
const char* escape_begin = contents.begin();
|
||||
if (!contents.consume_front(escape)) {
|
||||
// This is not an escape sequence, just a raw `\`.
|
||||
AppendChar(buffer_cursor, contents.front());
|
||||
@@ -528,7 +568,8 @@ static auto ExpandEscapeSequencesAndRemoveIndent(
|
||||
}
|
||||
|
||||
// Handle this escape sequence.
|
||||
ExpandAndConsumeEscapeSequence(emitter, contents, buffer_cursor);
|
||||
ExpandAndConsumeEscapeSequence(emitter, escape_begin, contents,
|
||||
buffer_cursor);
|
||||
buffer_last_escape = buffer_cursor;
|
||||
}
|
||||
}
|
||||
@@ -540,7 +581,7 @@ static auto IsControlCharacter(llvm::UTF32 c) -> bool {
|
||||
}
|
||||
|
||||
auto StringLiteral::ComputeCharLiteralValue(
|
||||
Diagnostics::Emitter<const char*>& emitter) const
|
||||
Diagnostics::Emitter<SourceLoc>& emitter) const
|
||||
-> std::optional<CharLiteralValue> {
|
||||
CARBON_DCHECK(kind_ == Kind::Char);
|
||||
CARBON_DCHECK(is_terminated_);
|
||||
@@ -548,7 +589,9 @@ auto StringLiteral::ComputeCharLiteralValue(
|
||||
if (hash_level_ != 0) {
|
||||
CARBON_DIAGNOSTIC(CharLiteralRaw, Error,
|
||||
"unexpected `#` before character literal");
|
||||
emitter.Emit(text_.begin(), CharLiteralRaw);
|
||||
emitter.Build(text_.begin(), CharLiteralRaw)
|
||||
.Attach(SourceLoc::Range(text_))
|
||||
.Emit();
|
||||
}
|
||||
|
||||
// Allocate a buffer sized to the content. Note it's possible this could be
|
||||
@@ -577,7 +620,10 @@ auto StringLiteral::ComputeCharLiteralValue(
|
||||
case llvm::conversionOK: {
|
||||
if (target_cursor == target) {
|
||||
CARBON_DIAGNOSTIC(CharLiteralEmpty, Error, "empty character literal");
|
||||
emitter.Emit(text_.begin(), CharLiteralEmpty);
|
||||
// The character goes between the quotes.
|
||||
emitter.Build(SourceLoc::Before(content_.begin()), CharLiteralEmpty)
|
||||
.Attach(SourceLoc::Range(text_))
|
||||
.Emit();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -593,7 +639,9 @@ auto StringLiteral::ComputeCharLiteralValue(
|
||||
"control character in character literal; specify as "
|
||||
"escape sequence `\\u{{{0:X-2}}`",
|
||||
llvm::UTF32);
|
||||
emitter.Emit(text_.begin(), CharLiteralControlCharacter, result);
|
||||
emitter.Build(text_.begin(), CharLiteralControlCharacter, result)
|
||||
.Attach(SourceLoc::Range(text_))
|
||||
.Emit();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -602,7 +650,9 @@ auto StringLiteral::ComputeCharLiteralValue(
|
||||
"escape sequence `\\x` in character literal; specify "
|
||||
"as escape sequence `\\u{{{0:X-2}}`",
|
||||
llvm::UTF32);
|
||||
emitter.Emit(text_.begin(), CharLiteralHexEscape, result);
|
||||
emitter.Build(text_.begin(), CharLiteralHexEscape, result)
|
||||
.Attach(SourceLoc::Range(text_))
|
||||
.Emit();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -610,18 +660,24 @@ auto StringLiteral::ComputeCharLiteralValue(
|
||||
}
|
||||
case llvm::sourceExhausted: {
|
||||
CARBON_DIAGNOSTIC(CharLiteralUnderflow, Error, "incomplete UTF-8");
|
||||
emitter.Emit(text_.begin(), CharLiteralUnderflow);
|
||||
emitter.Build(text_.begin(), CharLiteralUnderflow)
|
||||
.Attach(SourceLoc::Range(text_))
|
||||
.Emit();
|
||||
return std::nullopt;
|
||||
}
|
||||
case llvm::targetExhausted: {
|
||||
CARBON_DIAGNOSTIC(CharLiteralOverflow, Error, "too many characters");
|
||||
emitter.Emit(text_.begin(), CharLiteralOverflow);
|
||||
emitter.Build(text_.begin(), CharLiteralOverflow)
|
||||
.Attach(SourceLoc::Range(text_))
|
||||
.Emit();
|
||||
return std::nullopt;
|
||||
}
|
||||
case llvm::sourceIllegal: {
|
||||
CARBON_DIAGNOSTIC(CharLiteralInvalidUTF8, Error,
|
||||
"invalid UTF-8 character");
|
||||
emitter.Emit(text_.begin(), CharLiteralInvalidUTF8);
|
||||
emitter.Build(text_.begin(), CharLiteralInvalidUTF8)
|
||||
.Attach(SourceLoc::Range(text_))
|
||||
.Emit();
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
@@ -637,7 +693,9 @@ auto StringLiteral::ComputeStringValue(llvm::BumpPtrAllocator& allocator,
|
||||
CARBON_DIAGNOSTIC(
|
||||
MultiLineStringWithDoubleQuotes, Error,
|
||||
"use `'''` delimiters for a multi-line string literal, not `\"\"\"`");
|
||||
emitter.Emit(text_.begin(), MultiLineStringWithDoubleQuotes);
|
||||
emitter.Build(text_.begin(), MultiLineStringWithDoubleQuotes)
|
||||
.Attach(SourceLoc::Range(text_.take_front(hash_level_ + 3)))
|
||||
.Emit();
|
||||
}
|
||||
llvm::StringRef indent = IsMultiLine(kind_)
|
||||
? CheckIndent(emitter, text_, content_)
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include "toolchain/diagnostics/emitter.h"
|
||||
#include "toolchain/lex/source_loc.h"
|
||||
#include "toolchain/lex/token_info.h"
|
||||
|
||||
namespace Carbon::Lex {
|
||||
@@ -41,7 +42,7 @@ class StringLiteral {
|
||||
// Expand any escape sequences and compute the resulting character. This
|
||||
// handles error recovery internally, but can return nullopt for an invalid
|
||||
// character.
|
||||
auto ComputeCharLiteralValue(Diagnostics::Emitter<const char*>& emitter) const
|
||||
auto ComputeCharLiteralValue(Diagnostics::Emitter<SourceLoc>& emitter) const
|
||||
-> std::optional<CharLiteralValue>;
|
||||
|
||||
// Expand any escape sequences in the given string literal and compute the
|
||||
@@ -51,7 +52,7 @@ class StringLiteral {
|
||||
// deal with, this can return the content directly. Otherwise, the allocator
|
||||
// will be used for the StringRef.
|
||||
auto ComputeStringValue(llvm::BumpPtrAllocator& allocator,
|
||||
Diagnostics::Emitter<const char*>& emitter) const
|
||||
Diagnostics::Emitter<SourceLoc>& emitter) const
|
||||
-> llvm::StringRef;
|
||||
|
||||
// Get the text corresponding to this literal.
|
||||
|
||||
@@ -99,7 +99,7 @@ static void BM_SimpleStringValue(benchmark::State& state, int size,
|
||||
x.append(terminator);
|
||||
for (auto _ : state) {
|
||||
StringLiteral::Lex(x)->ComputeStringValue(
|
||||
allocator, Diagnostics::NullEmitter<const char*>());
|
||||
allocator, Diagnostics::NullEmitter<Lex::SourceLoc>());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto* null_emitter = &Diagnostics::NullEmitter<const char*>();
|
||||
auto* null_emitter = &Diagnostics::NullEmitter<Lex::SourceLoc>();
|
||||
if (literal->kind() == Lex::StringLiteral::Kind::Char) {
|
||||
volatile auto value = literal->ComputeCharLiteralValue(*null_emitter);
|
||||
(void)value;
|
||||
|
||||
@@ -12,12 +12,14 @@
|
||||
#include "common/check.h"
|
||||
#include "common/string_helpers.h"
|
||||
#include "toolchain/diagnostics/emitter.h"
|
||||
#include "toolchain/lex/source_loc.h"
|
||||
|
||||
namespace Carbon::Testing {
|
||||
|
||||
// A diagnostic converter for tests that lex a single token. Produces
|
||||
// locations such as "`12.5`:1:3" to refer to the third character in the token.
|
||||
class SingleTokenDiagnosticEmitter : public Diagnostics::Emitter<const char*> {
|
||||
class SingleTokenDiagnosticEmitter
|
||||
: public Diagnostics::Emitter<Lex::SourceLoc> {
|
||||
public:
|
||||
// Form a converter for a given token. The string provided here must refer
|
||||
// to the same character array that we are going to lex.
|
||||
@@ -27,8 +29,9 @@ class SingleTokenDiagnosticEmitter : public Diagnostics::Emitter<const char*> {
|
||||
|
||||
protected:
|
||||
// Implements `DiagnosticConverter::ConvertLoc`.
|
||||
auto ConvertLoc(const char* pos, ContextFnT /*context_fn*/) const
|
||||
auto ConvertLoc(Lex::SourceLoc loc, ContextFnT /*context_fn*/) const
|
||||
-> Diagnostics::ConvertedLoc override {
|
||||
const char* pos = loc.text().begin();
|
||||
CARBON_CHECK(StringRefContainsPointer(token_, pos),
|
||||
"invalid diagnostic location");
|
||||
llvm::StringRef prefix = token_.take_front(pos - token_.begin());
|
||||
@@ -37,7 +40,8 @@ class SingleTokenDiagnosticEmitter : public Diagnostics::Emitter<const char*> {
|
||||
// On first line.
|
||||
return {.loc = {.line_number = 1,
|
||||
.column_number =
|
||||
static_cast<int32_t>(pos - token_.begin() + 1)},
|
||||
static_cast<int32_t>(pos - token_.begin() + 1),
|
||||
.length = static_cast<int32_t>(loc.text().size())},
|
||||
.last_byte_offset = -1};
|
||||
} else {
|
||||
// On second or subsequent lines. Note that the line number here is 2
|
||||
@@ -46,7 +50,8 @@ class SingleTokenDiagnosticEmitter : public Diagnostics::Emitter<const char*> {
|
||||
return {
|
||||
.loc = {.line_number =
|
||||
static_cast<int32_t>(before_last_newline.count('\n') + 2),
|
||||
.column_number = static_cast<int32_t>(this_line.size() + 1)},
|
||||
.column_number = static_cast<int32_t>(this_line.size() + 1),
|
||||
.length = static_cast<int32_t>(loc.text().size())},
|
||||
.last_byte_offset = -1};
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -53,7 +53,7 @@
|
||||
'\xC3\xFF'
|
||||
// CHECK:STDOUT: - { index: 4, kind: "Error", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "'\\xC3\\xFF'", has_leading_space: true }
|
||||
|
||||
// CHECK:STDERR: fail_invalid.carbon:[[@LINE+2]]:1: error: empty character literal [CharLiteralEmpty]
|
||||
// CHECK:STDERR: fail_invalid.carbon:[[@LINE+2]]:2: error: empty character literal [CharLiteralEmpty]
|
||||
// CHECK:STDERR:
|
||||
''
|
||||
// CHECK:STDOUT: - { index: 5, kind: "Error", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "''", has_leading_space: true }
|
||||
@@ -75,7 +75,7 @@
|
||||
|
||||
// CHECK:STDERR: fail_invalid.carbon:[[@LINE+4]]:1: error: unexpected `#` before character literal [CharLiteralRaw]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_invalid.carbon:[[@LINE+2]]:1: error: empty character literal [CharLiteralEmpty]
|
||||
// CHECK:STDERR: fail_invalid.carbon:[[@LINE+2]]:4: error: empty character literal [CharLiteralEmpty]
|
||||
// CHECK:STDERR:
|
||||
##''##
|
||||
// CHECK:STDOUT: - { index: 9, kind: "Error", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "##''##", has_leading_space: true }
|
||||
@@ -94,17 +94,17 @@
|
||||
##'#\'##
|
||||
// CHECK:STDOUT: - { index: 11, kind: "Error", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "##'#\\'##", has_leading_space: true }
|
||||
|
||||
// CHECK:STDERR: fail_invalid.carbon:[[@LINE+2]]:1: error: character literal is missing a terminator [UnterminatedString]
|
||||
// CHECK:STDERR: fail_invalid.carbon:[[@LINE+2]]:2: error: character literal is missing a terminator [UnterminatedString]
|
||||
// CHECK:STDERR:
|
||||
'
|
||||
// CHECK:STDOUT: - { index: 12, kind: "Error", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "'", has_leading_space: true }
|
||||
|
||||
// CHECK:STDERR: fail_invalid.carbon:[[@LINE+2]]:1: error: character literal is missing a terminator [UnterminatedString]
|
||||
// CHECK:STDERR: fail_invalid.carbon:[[@LINE+2]]:4: error: character literal is missing a terminator [UnterminatedString]
|
||||
// CHECK:STDERR:
|
||||
'\'
|
||||
// CHECK:STDOUT: - { index: 13, kind: "Error", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "'\\'", has_leading_space: true }
|
||||
|
||||
// CHECK:STDERR: fail_invalid.carbon:[[@LINE+2]]:1: error: character literal is missing a terminator [UnterminatedString]
|
||||
// CHECK:STDERR: fail_invalid.carbon:[[@LINE+2]]:3: error: character literal is missing a terminator [UnterminatedString]
|
||||
// CHECK:STDERR:
|
||||
'\
|
||||
// CHECK:STDOUT: - { index: 14, kind: "Error", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "'\\", has_leading_space: true }
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lex/testdata/fail_malformed_escapes.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lex/testdata/fail_malformed_escapes.carbon
|
||||
|
||||
// Each diagnostic is located where the escape stops being well-formed, and
|
||||
// marks the escape up to there.
|
||||
|
||||
// --- fail_hex_escape_wrong_digit.carbon
|
||||
// CHECK:STDOUT: - filename: fail_hex_escape_wrong_digit.carbon
|
||||
// CHECK:STDOUT: tokens:
|
||||
|
||||
// CHECK:STDERR: fail_hex_escape_wrong_digit.carbon:[[@LINE+2]]:4: error: escape sequence `\x` must be followed by two uppercase hexadecimal digits, for example `\x0F` [HexadecimalEscapeMissingDigits]
|
||||
// CHECK:STDERR:
|
||||
"\xG1"
|
||||
// CHECK:STDOUT: - { index: 1, kind: "StringLiteral", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "\"\\xG1\"", value: "xG1", has_leading_space: true }
|
||||
|
||||
// --- fail_hex_escape_one_digit.carbon
|
||||
// CHECK:STDOUT: - filename: fail_hex_escape_one_digit.carbon
|
||||
// CHECK:STDOUT: tokens:
|
||||
|
||||
// CHECK:STDERR: fail_hex_escape_one_digit.carbon:[[@LINE+2]]:5: error: escape sequence `\x` must be followed by two uppercase hexadecimal digits, for example `\x0F` [HexadecimalEscapeMissingDigits]
|
||||
// CHECK:STDERR:
|
||||
"\x0g"
|
||||
// CHECK:STDOUT: - { index: 1, kind: "StringLiteral", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "\"\\x0g\"", value: "x0g", has_leading_space: true }
|
||||
|
||||
// --- fail_unicode_escape_empty_braces.carbon
|
||||
// CHECK:STDOUT: - filename: fail_unicode_escape_empty_braces.carbon
|
||||
// CHECK:STDOUT: tokens:
|
||||
|
||||
// CHECK:STDERR: fail_unicode_escape_empty_braces.carbon:[[@LINE+2]]:5: error: escape sequence `\u` must be followed by a braced sequence of uppercase hexadecimal digits, for example `\u{70AD}}` [UnicodeEscapeMissingBracedDigits]
|
||||
// CHECK:STDERR:
|
||||
"\u{}"
|
||||
// CHECK:STDOUT: - { index: 1, kind: "StringLiteral", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "\"\\u{}\"", value: "u{}", has_leading_space: true }
|
||||
|
||||
// --- fail_unicode_escape_unclosed.carbon
|
||||
// CHECK:STDOUT: - filename: fail_unicode_escape_unclosed.carbon
|
||||
// CHECK:STDOUT: tokens:
|
||||
|
||||
// CHECK:STDERR: fail_unicode_escape_unclosed.carbon:[[@LINE+2]]:7: error: escape sequence `\u` must be followed by a braced sequence of uppercase hexadecimal digits, for example `\u{70AD}}` [UnicodeEscapeMissingBracedDigits]
|
||||
// CHECK:STDERR:
|
||||
"\u{12"
|
||||
// CHECK:STDOUT: - { index: 1, kind: "StringLiteral", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "\"\\u{12\"", value: "u{12", has_leading_space: true }
|
||||
@@ -0,0 +1,38 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lex/testdata/fail_mismatched_brackets_line_end.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lex/testdata/fail_mismatched_brackets_line_end.carbon
|
||||
// CHECK:STDOUT: - filename: fail_mismatched_brackets_line_end.carbon
|
||||
// CHECK:STDOUT: tokens:
|
||||
|
||||
// A missing `{` goes at the end of the line of the token it follows, before the
|
||||
// comment there.
|
||||
fn F() {
|
||||
// CHECK:STDOUT: - { index: 1, kind: "Fn", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "fn", has_leading_space: true }
|
||||
// CHECK:STDOUT: - { index: 2, kind: "Identifier", line: {{ *}}[[@LINE-2]], column: 4, indent: 1, spelling: "F", identifier: 0, has_leading_space: true }
|
||||
// CHECK:STDOUT: - { index: 3, kind: "OpenParen", line: {{ *}}[[@LINE-3]], column: 5, indent: 1, spelling: "(", closing_token: 4 }
|
||||
// CHECK:STDOUT: - { index: 4, kind: "CloseParen", line: {{ *}}[[@LINE-4]], column: 6, indent: 1, spelling: ")", opening_token: 3 }
|
||||
// CHECK:STDOUT: - { index: 5, kind: "OpenCurlyBrace", line: {{ *}}[[@LINE-5]], column: 8, indent: 1, spelling: "{", closing_token: 16, has_leading_space: true }
|
||||
if (true) // A comment.
|
||||
// CHECK:STDOUT: - { index: 6, kind: "If", line: {{ *}}[[@LINE-1]], column: 3, indent: 3, spelling: "if", has_leading_space: true }
|
||||
// CHECK:STDOUT: - { index: 7, kind: "OpenParen", line: {{ *}}[[@LINE-2]], column: 6, indent: 3, spelling: "(", closing_token: 9, has_leading_space: true }
|
||||
// CHECK:STDOUT: - { index: 8, kind: "True", line: {{ *}}[[@LINE-3]], column: 7, indent: 3, spelling: "true" }
|
||||
// CHECK:STDOUT: - { index: 9, kind: "CloseParen", line: {{ *}}[[@LINE-4]], column: 11, indent: 3, spelling: ")", opening_token: 7 }
|
||||
// CHECK:STDOUT: - { index: 10, kind: "OpenCurlyBrace", line: {{ *}}[[@LINE-5]], column: 12, indent: 3, spelling: "{", closing_token: 15, has_leading_space: true, recovery: true }
|
||||
G();
|
||||
// CHECK:STDOUT: - { index: 11, kind: "Identifier", line: {{ *}}[[@LINE-1]], column: 5, indent: 5, spelling: "G", identifier: 1 }
|
||||
// CHECK:STDOUT: - { index: 12, kind: "OpenParen", line: {{ *}}[[@LINE-2]], column: 6, indent: 5, spelling: "(", closing_token: 13 }
|
||||
// CHECK:STDOUT: - { index: 13, kind: "CloseParen", line: {{ *}}[[@LINE-3]], column: 7, indent: 5, spelling: ")", opening_token: 12 }
|
||||
// CHECK:STDOUT: - { index: 14, kind: "Semi", line: {{ *}}[[@LINE-4]], column: 8, indent: 5, spelling: ";" }
|
||||
// CHECK:STDERR: fail_mismatched_brackets_line_end.carbon:[[@LINE+3]]:3: error: closing symbol without a corresponding opening symbol [UnmatchedClosing]
|
||||
// CHECK:STDERR: fail_mismatched_brackets_line_end.carbon:[[@LINE-12]]:12: note: possibly missing `{` here [PossiblyMissingBracketHere]
|
||||
// CHECK:STDERR:
|
||||
}
|
||||
// CHECK:STDOUT: - { index: 15, kind: "CloseCurlyBrace", line: {{ *}}[[@LINE-1]], column: 3, indent: 3, spelling: "}", opening_token: 10, has_leading_space: true }
|
||||
}
|
||||
// CHECK:STDOUT: - { index: 16, kind: "CloseCurlyBrace", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "}", opening_token: 5, has_leading_space: true }
|
||||
@@ -38,6 +38,7 @@ var s: String = '''py // A trailing comment with ', #, and " in it.
|
||||
// --- AUTOUPDATE-SPLIT
|
||||
|
||||
// CHECK:STDERR: fail_indent_mismatch.carbon:3:1: error: indentation does not match that of the closing `'''` in multi-line string literal [MismatchedIndentInString]
|
||||
// CHECK:STDERR: fail_indent_mismatch.carbon:4:1-2: note: closing `'''` is indented to here [ClosingDelimiterIndent]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_quotes.carbon:2:1: error: use `'''` delimiters for a multi-line string literal, not `"""` [MultiLineStringWithDoubleQuotes]
|
||||
// CHECK:STDERR:
|
||||
|
||||
+12
-3
@@ -12,7 +12,7 @@
|
||||
// CHECK:STDOUT: - filename: fail_decimal_escape.carbon
|
||||
// CHECK:STDOUT: tokens:
|
||||
|
||||
// CHECK:STDERR: fail_decimal_escape.carbon:[[@LINE+2]]:4: error: decimal digit follows `\0` escape sequence. Use `\x00` instead of `\0` if the next character is a digit [DecimalEscapeSequence]
|
||||
// CHECK:STDERR: fail_decimal_escape.carbon:[[@LINE+2]]:2: error: decimal digit follows `\0` escape sequence. Use `\x00` instead of `\0` if the next character is a digit [DecimalEscapeSequence]
|
||||
// CHECK:STDERR:
|
||||
"\01"
|
||||
// CHECK:STDOUT: - { index: 1, kind: "StringLiteral", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "\"\\01\"", value: "\x001", has_leading_space: true }
|
||||
@@ -21,7 +21,7 @@
|
||||
// CHECK:STDOUT: - filename: fail_unterminated.carbon
|
||||
// CHECK:STDOUT: tokens:
|
||||
|
||||
// CHECK:STDERR: fail_unterminated.carbon:[[@LINE+2]]:1: error: string literal is missing a terminator [UnterminatedString]
|
||||
// CHECK:STDERR: fail_unterminated.carbon:[[@LINE+2]]:3: error: string literal is missing a terminator [UnterminatedString]
|
||||
// CHECK:STDERR:
|
||||
"s
|
||||
// CHECK:STDOUT: - { index: 1, kind: "Error", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "\"s", has_leading_space: true }
|
||||
@@ -31,11 +31,20 @@
|
||||
// CHECK:STDOUT: - filename: fail_unknown_escape.carbon
|
||||
// CHECK:STDOUT: tokens:
|
||||
|
||||
// CHECK:STDERR: fail_unknown_escape.carbon:[[@LINE+2]]:3: error: unrecognized escape sequence `w` [UnknownEscapeSequence]
|
||||
// CHECK:STDERR: fail_unknown_escape.carbon:[[@LINE+2]]:2: error: unrecognized escape sequence `w` [UnknownEscapeSequence]
|
||||
// CHECK:STDERR:
|
||||
"\w"
|
||||
// CHECK:STDOUT: - { index: 1, kind: "StringLiteral", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "\"\\w\"", value: "w", has_leading_space: true }
|
||||
|
||||
// --- fail_raw_unknown_escape.carbon
|
||||
// CHECK:STDOUT: - filename: fail_raw_unknown_escape.carbon
|
||||
// CHECK:STDOUT: tokens:
|
||||
|
||||
// CHECK:STDERR: fail_raw_unknown_escape.carbon:[[@LINE+2]]:3: error: unrecognized escape sequence `w` [UnknownEscapeSequence]
|
||||
// CHECK:STDERR:
|
||||
#"\#w"#
|
||||
// CHECK:STDOUT: - { index: 1, kind: "StringLiteral", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "#\"\\#w\"#", value: "w", has_leading_space: true }
|
||||
|
||||
// --- fail_unicode_escape_missing_braced_digits.carbon
|
||||
// CHECK:STDOUT: - filename: fail_unicode_escape_missing_braced_digits.carbon
|
||||
// CHECK:STDOUT: tokens:
|
||||
|
||||
@@ -543,21 +543,30 @@ auto TokenizedBuffer::SourcePointerToDiagnosticLoc(const char* loc) const
|
||||
auto line_range = line_infos_.values();
|
||||
|
||||
// Find the first line starting after the given location.
|
||||
const auto next_line_it = llvm::partition_point(
|
||||
auto next_line_it = llvm::partition_point(
|
||||
line_range,
|
||||
[offset](const LineInfo& line) { return line.start <= offset; });
|
||||
|
||||
// Step back one line to find the line containing the given position.
|
||||
CARBON_CHECK(next_line_it != line_range.begin(),
|
||||
"location precedes the start of the first line");
|
||||
const auto line_it = std::prev(next_line_it);
|
||||
auto line_it = std::prev(next_line_it);
|
||||
|
||||
// A file that doesn't end in a newline has an empty line after its last one,
|
||||
// starting at the end of the file. The end of the file is the end of the
|
||||
// last line instead.
|
||||
llvm::StringRef text = source_->text();
|
||||
if (line_it->start == offset && line_it != line_range.begin() &&
|
||||
offset == static_cast<int32_t>(text.size()) && !text.ends_with("\n")) {
|
||||
next_line_it = line_it;
|
||||
--line_it;
|
||||
}
|
||||
int line_number = line_it - line_range.begin();
|
||||
int column_number = offset - line_it->start;
|
||||
|
||||
// Grab the line from the buffer by slicing from this line to the next
|
||||
// minus the newline. When on the last line, instead use the start to the end
|
||||
// of the buffer.
|
||||
llvm::StringRef text = source_->text();
|
||||
llvm::StringRef line = next_line_it != line_range.end()
|
||||
? text.slice(line_it->start, next_line_it->start)
|
||||
: text.substr(line_it->start);
|
||||
@@ -589,6 +598,43 @@ auto TokenizedBuffer::TokenToDiagnosticLoc(TokenIndex token) const
|
||||
return converted;
|
||||
}
|
||||
|
||||
auto TokenizedBuffer::TokenRangeToDiagnosticLoc(
|
||||
InclusiveTokenRange tokens) const -> Diagnostics::ConvertedLoc {
|
||||
auto begin_loc = TokenToDiagnosticLoc(tokens.begin);
|
||||
if (tokens.begin == tokens.end) {
|
||||
return begin_loc;
|
||||
}
|
||||
auto end_loc = TokenToDiagnosticLoc(tokens.end);
|
||||
begin_loc.last_byte_offset = end_loc.last_byte_offset;
|
||||
// TODO: A range across lines covers only the rest of its first line, because
|
||||
// a `Diagnostics::Loc` is on one line. Cover the whole range once a location
|
||||
// can span lines.
|
||||
if (begin_loc.loc.line_number != end_loc.loc.line_number) {
|
||||
begin_loc.loc.length =
|
||||
begin_loc.loc.line.size() - begin_loc.loc.column_number + 1;
|
||||
} else {
|
||||
if (begin_loc.loc.column_number != end_loc.loc.column_number) {
|
||||
begin_loc.loc.length = end_loc.loc.column_number + end_loc.loc.length -
|
||||
begin_loc.loc.column_number;
|
||||
}
|
||||
}
|
||||
return begin_loc;
|
||||
}
|
||||
|
||||
auto TokenizedBuffer::PointAfterTokenToDiagnosticLoc(TokenIndex token) const
|
||||
-> Diagnostics::ConvertedLoc {
|
||||
// A recovery token isn't in the source. It was inserted just past the token
|
||||
// before it, which puts the point just past it there too.
|
||||
while (IsRecoveryToken(token)) {
|
||||
token = TokenIndex(token.index - 1);
|
||||
}
|
||||
auto converted = SourcePointerToDiagnosticLoc(
|
||||
source_->text().begin() + token_infos_.Get(token).byte_offset() +
|
||||
GetTokenText(token).size());
|
||||
converted.loc.length = 0;
|
||||
return converted;
|
||||
}
|
||||
|
||||
auto TokenizedBuffer::OverlapsWithDumpSemIRRange(
|
||||
Lex::InclusiveTokenRange range) const -> bool {
|
||||
CARBON_CHECK(!dump_sem_ir_ranges_.empty());
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "toolchain/base/mem_usage.h"
|
||||
#include "toolchain/base/shared_value_stores.h"
|
||||
#include "toolchain/diagnostics/emitter.h"
|
||||
#include "toolchain/lex/source_loc.h"
|
||||
#include "toolchain/lex/token_index.h"
|
||||
#include "toolchain/lex/token_info.h"
|
||||
#include "toolchain/lex/token_kind.h"
|
||||
@@ -205,6 +206,11 @@ class TokenizedBuffer : public Printable<TokenizedBuffer> {
|
||||
// of the given word token. This should only be used for error recovery
|
||||
// purposes, when a phase after the lexer determines that a word token was
|
||||
// intended to represent an identifier rather than its special meaning.
|
||||
//
|
||||
// TODO: Recover with a raw identifier only when it names something, perhaps
|
||||
// with a note suggesting `r#` otherwise. The parser recovers with one for any
|
||||
// reserved word it takes as a name, so check cascades, for example to "name
|
||||
// `r#Cpp` not found" on `class Cpp.C {}`.
|
||||
auto AddPostLexingRecoveryTokenAsIdentifier(TokenIndex token) -> TokenIndex;
|
||||
|
||||
// Returns the 1-based indentation column number.
|
||||
@@ -251,6 +257,16 @@ class TokenizedBuffer : public Printable<TokenizedBuffer> {
|
||||
auto TokenToDiagnosticLoc(TokenIndex token) const
|
||||
-> Diagnostics::ConvertedLoc;
|
||||
|
||||
// Converts a run of tokens to a diagnostic location. A run spanning lines
|
||||
// covers the rest of its first line.
|
||||
auto TokenRangeToDiagnosticLoc(InclusiveTokenRange tokens) const
|
||||
-> Diagnostics::ConvertedLoc;
|
||||
|
||||
// Converts the point just past `token`, where something missing after it
|
||||
// would be written, to a diagnostic location.
|
||||
auto PointAfterTokenToDiagnosticLoc(TokenIndex token) const
|
||||
-> Diagnostics::ConvertedLoc;
|
||||
|
||||
// Returns true if the given range overlaps with an entry in
|
||||
// `dump_sem_ir_ranges_`. Must not be called when there are no ranges; query
|
||||
// `has_dump_sem_ir_ranges` first.
|
||||
@@ -294,17 +310,19 @@ class TokenizedBuffer : public Printable<TokenizedBuffer> {
|
||||
private:
|
||||
friend class Lexer;
|
||||
|
||||
class SourcePointerDiagnosticEmitter
|
||||
: public Diagnostics::Emitter<const char*> {
|
||||
class SourceLocDiagnosticEmitter : public Diagnostics::Emitter<SourceLoc> {
|
||||
public:
|
||||
explicit SourcePointerDiagnosticEmitter(Diagnostics::Consumer* consumer,
|
||||
const TokenizedBuffer* tokens)
|
||||
explicit SourceLocDiagnosticEmitter(Diagnostics::Consumer* consumer,
|
||||
const TokenizedBuffer* tokens)
|
||||
: Emitter(consumer), tokens_(tokens) {}
|
||||
|
||||
protected:
|
||||
auto ConvertLoc(const char* loc, ContextFnT /*context_fn*/) const
|
||||
auto ConvertLoc(SourceLoc loc, ContextFnT /*context_fn*/) const
|
||||
-> Diagnostics::ConvertedLoc override {
|
||||
return tokens_->SourcePointerToDiagnosticLoc(loc);
|
||||
auto converted =
|
||||
tokens_->SourcePointerToDiagnosticLoc(loc.text().begin());
|
||||
converted.loc.length = loc.text().size();
|
||||
return converted;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -1188,9 +1188,14 @@ TEST_F(LexerTest, TypeLiterals) {
|
||||
TEST_F(LexerTest, TypeLiteralTooManyDigits) {
|
||||
// We increase the number of digits until the first one that is to large.
|
||||
Testing::MockDiagnosticConsumer consumer;
|
||||
EXPECT_CALL(consumer, HandleDiagnostic(IsSingleDiagnostic(
|
||||
Diagnostics::Kind::TooManyTypeBitWidthDigits,
|
||||
Diagnostics::Level::Error, 1, 2, _)));
|
||||
// The bit width's digits are marked as the range that is too long.
|
||||
EXPECT_CALL(
|
||||
consumer,
|
||||
HandleDiagnostic(Testing::IsDiagnosticWithRange(
|
||||
Diagnostics::Kind::TooManyTypeBitWidthDigits,
|
||||
Diagnostics::Level::Error,
|
||||
{.line_number = 1, .column_number = 2, .length = 1}, _,
|
||||
{.line_number = 1, .column_number = 2, .length = testing::Gt(1)})));
|
||||
std::string code = "i";
|
||||
// A 128-bit APInt should be plenty large, but if needed in the future it can
|
||||
// be widened without issue.
|
||||
@@ -1221,10 +1226,13 @@ TEST_F(LexerTest, TypeLiteralTooManyDigits) {
|
||||
// Make sure we can also gracefully reject very large number of digits without
|
||||
// crashing or hanging, and show the correct number.
|
||||
constexpr int Count = 10000;
|
||||
EXPECT_CALL(consumer, HandleDiagnostic(IsSingleDiagnostic(
|
||||
Diagnostics::Kind::TooManyTypeBitWidthDigits,
|
||||
Diagnostics::Level::Error, 1, 2,
|
||||
HasSubstr(llvm::formatv(" {0} ", Count)))));
|
||||
EXPECT_CALL(consumer,
|
||||
HandleDiagnostic(Testing::IsDiagnosticWithRange(
|
||||
Diagnostics::Kind::TooManyTypeBitWidthDigits,
|
||||
Diagnostics::Level::Error,
|
||||
{.line_number = 1, .column_number = 2, .length = 1},
|
||||
HasSubstr(llvm::formatv(" {0} ", Count)),
|
||||
{.line_number = 1, .column_number = 2, .length = Count})));
|
||||
code = "i";
|
||||
code.append(Count, '9');
|
||||
auto& buffer = compile_helper_.GetTokenizedBuffer(code, &consumer);
|
||||
@@ -1328,8 +1336,9 @@ TEST_F(LexerTest, InvalidCommentRunsLumpTogether) {
|
||||
// no matter which invalid byte follows each `//`; a valid comment ends the
|
||||
// run and starts its own record.
|
||||
Testing::MockDiagnosticConsumer consumer;
|
||||
// The whitespace goes just past the `//`.
|
||||
EXPECT_CALL(consumer,
|
||||
HandleDiagnostic(IsSingleDiagnostic(
|
||||
HandleDiagnostic(Testing::IsPointDiagnostic(
|
||||
Diagnostics::Kind::NoWhitespaceAfterCommentIntroducer,
|
||||
Diagnostics::Level::Error, 1, 3, _)));
|
||||
auto& buffer = compile_helper_.GetTokenizedBuffer(
|
||||
@@ -1369,8 +1378,9 @@ TEST_F(LexerTest, InvalidCommentRunAtEof) {
|
||||
|
||||
TEST_F(LexerTest, DiagnosticWhitespace) {
|
||||
Testing::MockDiagnosticConsumer consumer;
|
||||
// The whitespace goes just past the `//`.
|
||||
EXPECT_CALL(consumer,
|
||||
HandleDiagnostic(IsSingleDiagnostic(
|
||||
HandleDiagnostic(Testing::IsPointDiagnostic(
|
||||
Diagnostics::Kind::NoWhitespaceAfterCommentIntroducer,
|
||||
Diagnostics::Level::Error, 1, 3, _)));
|
||||
compile_helper_.GetTokenizedBuffer("//no space after comment", &consumer);
|
||||
@@ -1378,23 +1388,34 @@ TEST_F(LexerTest, DiagnosticWhitespace) {
|
||||
|
||||
TEST_F(LexerTest, DiagnosticUnrecognizedEscape) {
|
||||
Testing::MockDiagnosticConsumer consumer;
|
||||
EXPECT_CALL(consumer,
|
||||
HandleDiagnostic(IsSingleDiagnostic(
|
||||
Diagnostics::Kind::UnknownEscapeSequence,
|
||||
Diagnostics::Level::Error, 1, 8, HasSubstr("`b`"))));
|
||||
// The message is located at the `\` that starts the escape, and the escape
|
||||
// is marked.
|
||||
EXPECT_CALL(
|
||||
consumer,
|
||||
HandleDiagnostic(Testing::IsDiagnosticWithRange(
|
||||
Diagnostics::Kind::UnknownEscapeSequence, Diagnostics::Level::Error,
|
||||
{.line_number = 1, .column_number = 7, .length = 1}, HasSubstr("`b`"),
|
||||
{.line_number = 1, .column_number = 7, .length = 2})));
|
||||
compile_helper_.GetTokenizedBuffer(R"("hello\bworld")", &consumer);
|
||||
}
|
||||
|
||||
TEST_F(LexerTest, DiagnosticBadHex) {
|
||||
Testing::MockDiagnosticConsumer consumer;
|
||||
EXPECT_CALL(consumer, HandleDiagnostic(IsSingleDiagnostic(
|
||||
Diagnostics::Kind::HexadecimalEscapeMissingDigits,
|
||||
Diagnostics::Level::Error, 1, 9, _)));
|
||||
// The message is located at the first digit that isn't an uppercase
|
||||
// hexadecimal digit, and the escape before it is marked.
|
||||
EXPECT_CALL(consumer,
|
||||
HandleDiagnostic(Testing::IsDiagnosticWithRange(
|
||||
Diagnostics::Kind::HexadecimalEscapeMissingDigits,
|
||||
Diagnostics::Level::Error,
|
||||
{.line_number = 1, .column_number = 9, .length = 0}, _,
|
||||
{.line_number = 1, .column_number = 7, .length = 2})));
|
||||
compile_helper_.GetTokenizedBuffer(R"("hello\xabworld")", &consumer);
|
||||
}
|
||||
|
||||
TEST_F(LexerTest, DiagnosticInvalidDigit) {
|
||||
Testing::MockDiagnosticConsumer consumer;
|
||||
// Only the invalid digit is marked, since the message says which literal it
|
||||
// is in.
|
||||
EXPECT_CALL(consumer,
|
||||
HandleDiagnostic(IsSingleDiagnostic(
|
||||
Diagnostics::Kind::InvalidDigit, Diagnostics::Level::Error, 1,
|
||||
@@ -1420,17 +1441,82 @@ TEST_F(LexerTest, DiagnosticLfCr) {
|
||||
|
||||
TEST_F(LexerTest, DiagnosticMissingTerminator) {
|
||||
Testing::MockDiagnosticConsumer consumer;
|
||||
EXPECT_CALL(consumer, HandleDiagnostic(IsSingleDiagnostic(
|
||||
Diagnostics::Kind::UnterminatedString,
|
||||
Diagnostics::Level::Error, 1, 1, _)));
|
||||
// The terminator goes where the literal stops, and the whole literal is
|
||||
// marked.
|
||||
EXPECT_CALL(
|
||||
consumer,
|
||||
HandleDiagnostic(Testing::IsDiagnosticWithRange(
|
||||
Diagnostics::Kind::UnterminatedString, Diagnostics::Level::Error,
|
||||
{.line_number = 1, .column_number = 5, .length = 0}, _,
|
||||
{.line_number = 1, .column_number = 1, .length = 4})));
|
||||
compile_helper_.GetTokenizedBuffer(R"(#" ")", &consumer);
|
||||
}
|
||||
|
||||
TEST_F(LexerTest, DiagnosticMissingMultiLineTerminator) {
|
||||
Testing::MockDiagnosticConsumer consumer;
|
||||
// The literal runs to the end of the file, which is where the terminator
|
||||
// goes, and the literal is marked from its introducer.
|
||||
EXPECT_CALL(
|
||||
consumer,
|
||||
HandleDiagnostic(Testing::IsDiagnosticWithRange(
|
||||
Diagnostics::Kind::UnterminatedString, Diagnostics::Level::Error,
|
||||
{.line_number = 3, .column_number = 1, .length = 0}, _,
|
||||
{.line_number = 1, .column_number = 1, .length = _})));
|
||||
compile_helper_.GetTokenizedBuffer(
|
||||
"'''\n"
|
||||
" text\n",
|
||||
&consumer);
|
||||
}
|
||||
|
||||
TEST_F(LexerTest, DiagnosticPointAtEndOfFile) {
|
||||
Testing::MockDiagnosticConsumer consumer;
|
||||
// A file that doesn't end in a newline ends at the end of its last line.
|
||||
EXPECT_CALL(consumer, HandleDiagnostic(Testing::IsPointDiagnostic(
|
||||
Diagnostics::Kind::DollarIntLiteralMissingNumber,
|
||||
Diagnostics::Level::Error, 1, 2, _)));
|
||||
compile_helper_.GetTokenizedBuffer("$", &consumer);
|
||||
}
|
||||
|
||||
// Returns the line and column of the point just past `token`, checking that it
|
||||
// is a point.
|
||||
static auto PointAfter(const TokenizedBuffer& buffer, int token)
|
||||
-> std::pair<int, int> {
|
||||
auto loc = buffer.PointAfterTokenToDiagnosticLoc(TokenIndex(token)).loc;
|
||||
EXPECT_THAT(loc.length, Eq(0));
|
||||
return {loc.line_number, loc.column_number};
|
||||
}
|
||||
|
||||
TEST_F(LexerTest, PointAfterToken) {
|
||||
// Tokens are `FileStart`, `a`, `b`, the string literal, and `FileEnd`.
|
||||
auto& buffer = compile_helper_.GetTokenizedBuffer(
|
||||
"a b\n"
|
||||
"'''\n"
|
||||
" c\n"
|
||||
"'''");
|
||||
// The point is before the spaces after the token.
|
||||
EXPECT_THAT(PointAfter(buffer, 1), Pair(1, 2));
|
||||
// The point just past the last token on a line is at the end of the line.
|
||||
EXPECT_THAT(PointAfter(buffer, 2), Pair(1, 5));
|
||||
// A token spanning lines ends on its last line, which here ends the file.
|
||||
EXPECT_THAT(PointAfter(buffer, 3), Pair(4, 4));
|
||||
}
|
||||
|
||||
TEST_F(LexerTest, PointAfterRecoveryToken) {
|
||||
// The lexer inserts a `)` after `a`. It isn't in the source, so the point
|
||||
// just past it is the point just past `a`.
|
||||
auto& buffer = compile_helper_.GetTokenizedBuffer("(a");
|
||||
ASSERT_TRUE(buffer.IsRecoveryToken(TokenIndex(3)));
|
||||
EXPECT_THAT(PointAfter(buffer, 3), Pair(1, 3));
|
||||
}
|
||||
|
||||
TEST_F(LexerTest, DiagnosticUnrecognizedChar) {
|
||||
Testing::MockDiagnosticConsumer consumer;
|
||||
EXPECT_CALL(consumer, HandleDiagnostic(IsSingleDiagnostic(
|
||||
Diagnostics::Kind::UnrecognizedCharacters,
|
||||
Diagnostics::Level::Error, 1, 1, _)));
|
||||
EXPECT_CALL(
|
||||
consumer,
|
||||
HandleDiagnostic(Testing::IsDiagnosticWithRange(
|
||||
Diagnostics::Kind::UnrecognizedCharacters, Diagnostics::Level::Error,
|
||||
{.line_number = 1, .column_number = 1, .length = 1}, _,
|
||||
{.line_number = 1, .column_number = 1, .length = 1})));
|
||||
compile_helper_.GetTokenizedBuffer("\b", &consumer);
|
||||
}
|
||||
|
||||
|
||||
+42
-14
@@ -55,11 +55,20 @@ auto Context::VlogPopState(llvm::StringLiteral label) const -> void {
|
||||
state_stack_.back());
|
||||
}
|
||||
|
||||
auto Context::OpeningBracketInsertionPoint() const -> DiagnosticLoc {
|
||||
auto previous_token = *(position_ - 1);
|
||||
if (tokens().GetEndLoc(previous_token).first ==
|
||||
tokens().GetLine(*position_)) {
|
||||
return DiagnosticLoc::Before(*position_);
|
||||
}
|
||||
return DiagnosticLoc::After(previous_token);
|
||||
}
|
||||
|
||||
auto Context::DiagnoseExpectedOpenCurlyBrace(Lex::TokenIndex default_token,
|
||||
NodeKind start_kind) -> void {
|
||||
CARBON_DIAGNOSTIC(ExpectedCurlyBraceAfter, Error, "expected `{` after `{0}`",
|
||||
Lex::TokenKind);
|
||||
emitter_.Emit(*position_, ExpectedCurlyBraceAfter,
|
||||
emitter_.Emit(OpeningBracketInsertionPoint(), ExpectedCurlyBraceAfter,
|
||||
tokens().GetKind(default_token));
|
||||
AddLeafNode(start_kind, default_token, /*has_error=*/true);
|
||||
}
|
||||
@@ -68,7 +77,7 @@ auto Context::DiagnoseExpectedOpenParen(Lex::TokenIndex default_token,
|
||||
NodeKind start_kind) -> void {
|
||||
CARBON_DIAGNOSTIC(ExpectedParenAfter, Error, "expected `(` after `{0}`",
|
||||
Lex::TokenKind);
|
||||
emitter_.Emit(*position_, ExpectedParenAfter,
|
||||
emitter_.Emit(OpeningBracketInsertionPoint(), ExpectedParenAfter,
|
||||
tokens().GetKind(default_token));
|
||||
AddLeafNode(start_kind, default_token, /*has_error=*/true);
|
||||
}
|
||||
@@ -81,14 +90,20 @@ auto Context::DiagnoseAndSkipToCloseSymbol(State state, NodeKind close_kind)
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Include the location of the matching opening delimiter in the
|
||||
// diagnostic.
|
||||
CARBON_DIAGNOSTIC(ExpectedCloseSymbol, Error,
|
||||
"unexpected tokens before `{0}`", Lex::TokenKind);
|
||||
emitter_.Emit(*position_, ExpectedCloseSymbol,
|
||||
open_token_kind.closing_symbol());
|
||||
CARBON_DIAGNOSTIC_LABEL(GroupOpenedHere, Info, "matching this `{0}`",
|
||||
Lex::TokenKind);
|
||||
auto matched_close = tokens().GetMatchedClosingToken(state.token);
|
||||
emitter_
|
||||
.Build(DiagnosticLoc::Tokens(
|
||||
{.begin = *position_,
|
||||
.end = Lex::TokenIndex(matched_close.index - 1)}),
|
||||
ExpectedCloseSymbol, open_token_kind.closing_symbol())
|
||||
.Attach(state.token, GroupOpenedHere, open_token_kind)
|
||||
.Emit();
|
||||
|
||||
SkipTo(tokens().GetMatchedClosingToken(state.token));
|
||||
SkipTo(matched_close);
|
||||
AddNode(close_kind, Consume(), /*has_error=*/true);
|
||||
}
|
||||
|
||||
@@ -184,13 +199,18 @@ auto Context::DiagnoseInvalidOperatorFixity(OperatorFixity fixity) -> void {
|
||||
"whitespace missing {0:=-1:before|=0:around|=1:after} "
|
||||
"binary operator",
|
||||
Diagnostics::IntAsSelect);
|
||||
// Whitespace missing on one side goes at that side. Whitespace missing
|
||||
// around the operator is about the operator.
|
||||
Diagnostics::IntAsSelect pos(0);
|
||||
DiagnosticLoc loc = *position_;
|
||||
if (tokens().HasLeadingWhitespace(*position_)) {
|
||||
pos.value = 1;
|
||||
loc = DiagnosticLoc::After(*position_);
|
||||
} else if (tokens().HasTrailingWhitespace(*position_)) {
|
||||
pos.value = -1;
|
||||
loc = DiagnosticLoc::Before(*position_);
|
||||
}
|
||||
emitter_.Emit(*position_, BinaryOperatorRequiresWhitespace, pos);
|
||||
emitter_.Emit(loc, BinaryOperatorRequiresWhitespace, pos);
|
||||
} else {
|
||||
bool prefix = fixity == OperatorFixity::Prefix;
|
||||
|
||||
@@ -209,7 +229,9 @@ auto Context::DiagnoseInvalidOperatorFixity(OperatorFixity fixity) -> void {
|
||||
UnaryOperatorRequiresWhitespace, Error,
|
||||
"whitespace is required {0:before|after} this unary operator",
|
||||
Diagnostics::BoolAsSelect);
|
||||
emitter_.Emit(*position_, UnaryOperatorRequiresWhitespace, prefix);
|
||||
emitter_.Emit(prefix ? DiagnosticLoc::Before(*position_)
|
||||
: DiagnosticLoc::After(*position_),
|
||||
UnaryOperatorRequiresWhitespace, prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -218,9 +240,13 @@ auto Context::RecoverFromInvalidListToken(Lex::TokenKind close_kind,
|
||||
bool already_has_error) -> void {
|
||||
// Don't error a second time on the same element.
|
||||
if (!already_has_error) {
|
||||
// TODO: Also label the bracket that opened the list, to show which list
|
||||
// this is. That needs the opening token, which callers hold in
|
||||
// `state.token` but don't pass down.
|
||||
CARBON_DIAGNOSTIC(UnexpectedTokenAfterListElement, Error,
|
||||
"expected `,` or `{0}`", Lex::TokenKind);
|
||||
emitter_.Emit(*position_, UnexpectedTokenAfterListElement, close_kind);
|
||||
emitter_.Emit(PointAfterPreviousToken(), UnexpectedTokenAfterListElement,
|
||||
close_kind);
|
||||
ReturnErrorOnState();
|
||||
}
|
||||
|
||||
@@ -279,8 +305,9 @@ auto Context::ParseLibraryName(bool accept_default)
|
||||
"expected `default` or a string literal to specify the library name");
|
||||
CARBON_DIAGNOSTIC(ExpectedLibraryName, Error,
|
||||
"expected a string literal to specify the library name");
|
||||
emitter().Emit(*position(), accept_default ? ExpectedLibraryNameOrDefault
|
||||
: ExpectedLibraryName);
|
||||
emitter().Emit(PointAfterPreviousToken(), accept_default
|
||||
? ExpectedLibraryNameOrDefault
|
||||
: ExpectedLibraryName);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -298,7 +325,7 @@ auto Context::ParseLibrarySpecifier(bool accept_default)
|
||||
auto Context::DiagnoseExpectedDeclSemi(Lex::TokenKind expected_kind) -> void {
|
||||
CARBON_DIAGNOSTIC(ExpectedDeclSemi, Error,
|
||||
"`{0}` declarations must end with a `;`", Lex::TokenKind);
|
||||
emitter().Emit(*position(), ExpectedDeclSemi, expected_kind);
|
||||
emitter().Emit(PointAfterPreviousToken(), ExpectedDeclSemi, expected_kind);
|
||||
}
|
||||
|
||||
auto Context::DiagnoseExpectedDeclSemiOrDefinition(Lex::TokenKind expected_kind)
|
||||
@@ -307,7 +334,8 @@ auto Context::DiagnoseExpectedDeclSemiOrDefinition(Lex::TokenKind expected_kind)
|
||||
"`{0}` declarations must either end with a `;` or "
|
||||
"have a `{{ ... }` block for a definition",
|
||||
Lex::TokenKind);
|
||||
emitter().Emit(*position(), ExpectedDeclSemiOrDefinition, expected_kind);
|
||||
emitter().Emit(PointAfterPreviousToken(), ExpectedDeclSemiOrDefinition,
|
||||
expected_kind);
|
||||
}
|
||||
|
||||
// Returns whether we are currently parsing in a scope in which function
|
||||
|
||||
@@ -45,23 +45,80 @@ enum class BindingContext : uint8_t {
|
||||
CompileTimeEntityParam,
|
||||
};
|
||||
|
||||
// The location of a parse diagnostic: a run of tokens, or the point just before
|
||||
// or just past a token, where something missing would be written.
|
||||
class DiagnosticLoc {
|
||||
public:
|
||||
enum class Kind : int8_t {
|
||||
Tokens,
|
||||
PointBefore,
|
||||
PointAfter,
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor): A token is a location.
|
||||
DiagnosticLoc(Lex::TokenIndex token)
|
||||
: DiagnosticLoc(Kind::Tokens, {.begin = token, .end = token}) {}
|
||||
|
||||
// The tokens from `tokens.begin` through `tokens.end`.
|
||||
static auto Tokens(Lex::InclusiveTokenRange tokens) -> DiagnosticLoc {
|
||||
return DiagnosticLoc(Kind::Tokens, tokens);
|
||||
}
|
||||
|
||||
// The point just before `token`.
|
||||
static auto Before(Lex::TokenIndex token) -> DiagnosticLoc {
|
||||
return DiagnosticLoc(Kind::PointBefore, {.begin = token, .end = token});
|
||||
}
|
||||
|
||||
// The point just past `token`.
|
||||
static auto After(Lex::TokenIndex token) -> DiagnosticLoc {
|
||||
return DiagnosticLoc(Kind::PointAfter, {.begin = token, .end = token});
|
||||
}
|
||||
|
||||
auto kind() const -> Kind { return kind_; }
|
||||
|
||||
// The tokens this location covers, or the one token a point is before or
|
||||
// after.
|
||||
auto tokens() const -> Lex::InclusiveTokenRange { return tokens_; }
|
||||
|
||||
private:
|
||||
explicit DiagnosticLoc(Kind kind, Lex::InclusiveTokenRange tokens)
|
||||
: kind_(kind), tokens_(tokens) {}
|
||||
|
||||
Kind kind_;
|
||||
Lex::InclusiveTokenRange tokens_;
|
||||
};
|
||||
|
||||
// Context and shared functionality for parser handlers. See state.def for state
|
||||
// documentation.
|
||||
class Context {
|
||||
public:
|
||||
// A token-based emitter for use during parse.
|
||||
class DiagnosticEmitter : public Diagnostics::Emitter<Lex::TokenIndex> {
|
||||
class DiagnosticEmitter : public Diagnostics::Emitter<DiagnosticLoc> {
|
||||
public:
|
||||
explicit DiagnosticEmitter(Diagnostics::Consumer* consumer,
|
||||
Context* context)
|
||||
: Emitter(consumer), context_(context) {}
|
||||
|
||||
protected:
|
||||
// Applies the `position_` to the `last_byte_offset` returned by
|
||||
// `TokenToDiagnosticLoc`.
|
||||
auto ConvertLoc(Lex::TokenIndex token, ContextFnT /*context_fn*/) const
|
||||
// Applies the `position_` to the `last_byte_offset` of the converted
|
||||
// location, so that a diagnostic sorts by how far the parser had read even
|
||||
// when the location it names is earlier. Diagnostics with the same offset
|
||||
// stay in the order they were emitted.
|
||||
auto ConvertLoc(DiagnosticLoc loc, ContextFnT /*context_fn*/) const
|
||||
-> Diagnostics::ConvertedLoc override {
|
||||
auto converted = context_->tokens().TokenToDiagnosticLoc(token);
|
||||
auto converted =
|
||||
context_->tokens().TokenRangeToDiagnosticLoc(loc.tokens());
|
||||
switch (loc.kind()) {
|
||||
case DiagnosticLoc::Kind::Tokens:
|
||||
break;
|
||||
case DiagnosticLoc::Kind::PointBefore:
|
||||
converted.loc.length = 0;
|
||||
break;
|
||||
case DiagnosticLoc::Kind::PointAfter:
|
||||
converted = context_->tokens().PointAfterTokenToDiagnosticLoc(
|
||||
loc.tokens().begin);
|
||||
break;
|
||||
}
|
||||
converted.last_byte_offset =
|
||||
std::max(converted.last_byte_offset,
|
||||
context_->tokens().GetByteOffset(*context_->position()));
|
||||
@@ -627,6 +684,17 @@ class Context {
|
||||
auto position() -> Lex::TokenIterator& { return position_; }
|
||||
auto position() const -> Lex::TokenIterator { return position_; }
|
||||
|
||||
// Returns the point just past the token before `position_`, which is where a
|
||||
// token missing at `position_` would be written.
|
||||
auto PointAfterPreviousToken() const -> DiagnosticLoc {
|
||||
return DiagnosticLoc::After(*(position_ - 1));
|
||||
}
|
||||
|
||||
// Returns where a user would insert an opening bracket missing at
|
||||
// `position_`: against the token at `position_` when it's on the line where
|
||||
// the previous token ends, and otherwise just past the previous token.
|
||||
auto OpeningBracketInsertionPoint() const -> DiagnosticLoc;
|
||||
|
||||
auto state_stack() -> llvm::SmallVector<State>& { return state_stack_; }
|
||||
|
||||
auto state_stack() const -> const llvm::SmallVector<State>& {
|
||||
|
||||
@@ -35,7 +35,8 @@ auto HandleAliasAfterName(Context& context) -> void {
|
||||
} else {
|
||||
CARBON_DIAGNOSTIC(ExpectedAliasInitializer, Error,
|
||||
"`alias` requires a `=` for the source");
|
||||
context.emitter().Emit(*context.position(), ExpectedAliasInitializer);
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
ExpectedAliasInitializer);
|
||||
context.RecoverFromDeclError(state, NodeKind::Alias,
|
||||
/*skip_past_likely_end=*/true);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@ auto HandleArrayExprComma(Context& context) -> void {
|
||||
context.AddLeafNode(NodeKind::ArrayExprComma, *context.position(), true);
|
||||
CARBON_DIAGNOSTIC(ExpectedArrayComma, Error,
|
||||
"expected `,` in `array(Type, Count)`");
|
||||
context.emitter().Emit(*context.position(), ExpectedArrayComma);
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
ExpectedArrayComma);
|
||||
state.has_error = true;
|
||||
}
|
||||
context.PushState(state, StateKind::ArrayExprFinish);
|
||||
|
||||
@@ -15,7 +15,8 @@ auto HandleBaseAfterIntroducer(Context& context) -> void {
|
||||
NodeKind::BaseColon)) {
|
||||
CARBON_DIAGNOSTIC(ExpectedAfterBase, Error,
|
||||
"`class` or `:` expected after `base`");
|
||||
context.emitter().Emit(*context.position(), ExpectedAfterBase);
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
ExpectedAfterBase);
|
||||
auto base_token = *(context.position() - 1);
|
||||
auto previous_token = Lex::TokenIndex(base_token.index - 1);
|
||||
if (context.tokens().GetKind(previous_token) != Lex::TokenKind::Extend) {
|
||||
|
||||
@@ -77,15 +77,16 @@ auto HandleBindingPattern(Context& context) -> void {
|
||||
auto state = context.PopState();
|
||||
|
||||
// Handle an invalid pattern introducer for parameters and variables.
|
||||
auto on_error = [&](bool expected_name, bool recover_as_raw = false) {
|
||||
auto on_error = [&](DiagnosticLoc loc, bool expected_name,
|
||||
bool recover_as_raw = false) {
|
||||
if (!state.has_error) {
|
||||
CARBON_DIAGNOSTIC(
|
||||
ExpectedBindingPattern, Error,
|
||||
"expected {0:name|`:` or `:?`} in binding pattern"
|
||||
"{1:; prefix reserved word with `r#` to form a valid identifier|}",
|
||||
Diagnostics::BoolAsSelect, Diagnostics::BoolAsSelect);
|
||||
context.emitter().Emit(*context.position(), ExpectedBindingPattern,
|
||||
expected_name, recover_as_raw);
|
||||
context.emitter().Emit(loc, ExpectedBindingPattern, expected_name,
|
||||
recover_as_raw);
|
||||
state.has_error = !recover_as_raw;
|
||||
}
|
||||
};
|
||||
@@ -135,8 +136,7 @@ auto HandleBindingPattern(Context& context) -> void {
|
||||
AnonymousBindingInStructPattern, Error,
|
||||
"anonymous binding found in struct pattern; use `.field = "
|
||||
"_: field_type` or `unused field: field_type`");
|
||||
context.emitter().Emit(*context.position(),
|
||||
AnonymousBindingInStructPattern);
|
||||
context.emitter().Emit(*underscore, AnonymousBindingInStructPattern);
|
||||
state.has_error = true;
|
||||
}
|
||||
context.AddLeafNode(NodeKind::UnderscoreName, *underscore);
|
||||
@@ -147,17 +147,22 @@ auto HandleBindingPattern(Context& context) -> void {
|
||||
// such as a numeric type literal or a keyword. For error recovery, convert
|
||||
// the token to an identifier, as we can be confident that a word in this
|
||||
// position was intended to be a declared name.
|
||||
auto word = context.Consume();
|
||||
auto word_as_identifier =
|
||||
context.tokens().AddPostLexingRecoveryTokenAsIdentifier(
|
||||
context.Consume());
|
||||
context.tokens().AddPostLexingRecoveryTokenAsIdentifier(word);
|
||||
context.AddLeafNode(NodeKind::IdentifierNameNotBeforeSignature,
|
||||
word_as_identifier);
|
||||
on_error(/*expected_name=*/true, /*recover_as_raw*/ true);
|
||||
on_error(word, /*expected_name=*/true, /*recover_as_raw*/ true);
|
||||
} else {
|
||||
// The missing name goes after the modifiers before it. With none, the
|
||||
// binding starts at the token found in the name's place.
|
||||
auto missing_name_loc = state.token == *context.position()
|
||||
? DiagnosticLoc(*context.position())
|
||||
: context.PointAfterPreviousToken();
|
||||
// Add a placeholder for the name.
|
||||
context.AddLeafNode(NodeKind::IdentifierNameNotBeforeSignature,
|
||||
*context.position(), /*has_error=*/true);
|
||||
on_error(/*expected_name=*/true);
|
||||
on_error(missing_name_loc, /*expected_name=*/true);
|
||||
}
|
||||
|
||||
auto token_kind = context.PositionKind();
|
||||
@@ -177,7 +182,7 @@ auto HandleBindingPattern(Context& context) -> void {
|
||||
}
|
||||
return;
|
||||
}
|
||||
on_error(/*expected_name=*/false);
|
||||
on_error(context.PointAfterPreviousToken(), /*expected_name=*/false);
|
||||
// Add a substitute for the identifier name and virtual type-start nodes.
|
||||
context.AddInvalidParse(*context.position());
|
||||
context.AddInvalidParse(*context.position());
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "toolchain/diagnostics/format_providers.h"
|
||||
#include "toolchain/parse/context.h"
|
||||
#include "toolchain/parse/handle.h"
|
||||
|
||||
@@ -23,7 +24,8 @@ auto HandleChoiceDefinitionStart(Context& context) -> void {
|
||||
if (!state.has_error) {
|
||||
CARBON_DIAGNOSTIC(ExpectedChoiceDefinition, Error,
|
||||
"choice definition expected");
|
||||
context.emitter().Emit(*context.position(), ExpectedChoiceDefinition);
|
||||
context.emitter().Emit(context.OpeningBracketInsertionPoint(),
|
||||
ExpectedChoiceDefinition);
|
||||
}
|
||||
|
||||
context.AddNode(NodeKind::ChoiceDefinitionStart, *context.position(),
|
||||
@@ -55,18 +57,30 @@ auto HandleChoiceAlternative(Context& context) -> void {
|
||||
|
||||
auto token = context.ConsumeIf(Lex::TokenKind::Identifier);
|
||||
if (!token) {
|
||||
// A word that can't be a name, followed by a token that can follow one,
|
||||
// was most likely meant as the name.
|
||||
bool recover_as_raw =
|
||||
context.PositionKind().is_word() &&
|
||||
context.PositionKind(Lookahead::NextToken)
|
||||
.IsOneOf({Lex::TokenKind::Comma, Lex::TokenKind::CloseCurlyBrace,
|
||||
Lex::TokenKind::OpenParen});
|
||||
if (!state.has_error) {
|
||||
CARBON_DIAGNOSTIC(ExpectedChoiceAlternativeName, Error,
|
||||
"expected choice alternative name");
|
||||
context.emitter().Emit(*context.position(),
|
||||
ExpectedChoiceAlternativeName);
|
||||
CARBON_DIAGNOSTIC(
|
||||
ExpectedChoiceAlternativeName, Error,
|
||||
"expected choice alternative name"
|
||||
"{0:; prefix reserved word with `r#` to form a valid identifier|}",
|
||||
Diagnostics::BoolAsSelect);
|
||||
context.emitter().Emit(*context.position(), ExpectedChoiceAlternativeName,
|
||||
recover_as_raw);
|
||||
}
|
||||
|
||||
context.SkipPastLikelyEnd(*context.position());
|
||||
|
||||
context.ReturnErrorOnState();
|
||||
|
||||
return;
|
||||
if (!recover_as_raw) {
|
||||
context.SkipPastLikelyEnd(*context.position());
|
||||
context.ReturnErrorOnState();
|
||||
return;
|
||||
}
|
||||
token = context.tokens().AddPostLexingRecoveryTokenAsIdentifier(
|
||||
context.Consume());
|
||||
}
|
||||
|
||||
if (context.PositionIs(Lex::TokenKind::OpenParen)) {
|
||||
|
||||
@@ -20,7 +20,8 @@ auto HandleCodeBlock(Context& context) -> void {
|
||||
|
||||
// Recover by parsing a single statement.
|
||||
CARBON_DIAGNOSTIC(ExpectedCodeBlock, Error, "expected braced code block");
|
||||
context.emitter().Emit(*context.position(), ExpectedCodeBlock);
|
||||
context.emitter().Emit(context.OpeningBracketInsertionPoint(),
|
||||
ExpectedCodeBlock);
|
||||
|
||||
context.PushState(StateKind::Statement);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "toolchain/diagnostics/format_providers.h"
|
||||
#include "toolchain/lex/token_index.h"
|
||||
#include "toolchain/parse/context.h"
|
||||
#include "toolchain/parse/handle.h"
|
||||
@@ -88,22 +89,44 @@ auto HandleDeclNameAndParams(Context& context) -> void {
|
||||
return;
|
||||
}
|
||||
|
||||
Lex::TokenIndex token = *context.position();
|
||||
if (context.tokens().GetKind(token) == Lex::TokenKind::FileEnd) {
|
||||
// The end of file is an unhelpful diagnostic location. Instead, use the
|
||||
// introducer token.
|
||||
token = state.token;
|
||||
}
|
||||
// A word that can't be a name, followed by a token that can follow one, was
|
||||
// most likely meant as the name, and is what the message is about. Otherwise
|
||||
// the name is missing, and goes after the token before it.
|
||||
bool recover_as_raw =
|
||||
context.PositionKind().is_word() &&
|
||||
context.PositionKind(Lookahead::NextToken)
|
||||
.IsOneOf({Lex::TokenKind::Period, Lex::TokenKind::OpenSquareBracket,
|
||||
Lex::TokenKind::OpenParen, Lex::TokenKind::MinusGreater,
|
||||
Lex::TokenKind::MinusGreaterQuestion, Lex::TokenKind::Semi,
|
||||
Lex::TokenKind::OpenCurlyBrace, Lex::TokenKind::Equal});
|
||||
auto loc = recover_as_raw ? DiagnosticLoc(*context.position())
|
||||
: context.PointAfterPreviousToken();
|
||||
if (state.token == *context.position()) {
|
||||
CARBON_DIAGNOSTIC(ExpectedDeclNameAfterPeriod, Error,
|
||||
"`.` should be followed by a name");
|
||||
context.emitter().Emit(token, ExpectedDeclNameAfterPeriod);
|
||||
CARBON_DIAGNOSTIC(
|
||||
ExpectedDeclNameAfterPeriod, Error,
|
||||
"`.` should be followed by a name"
|
||||
"{0:; prefix reserved word with `r#` to form a valid identifier|}",
|
||||
Diagnostics::BoolAsSelect);
|
||||
context.emitter().Emit(loc, ExpectedDeclNameAfterPeriod, recover_as_raw);
|
||||
} else {
|
||||
CARBON_DIAGNOSTIC(ExpectedDeclName, Error,
|
||||
"`{0}` introducer should be followed by a name",
|
||||
Lex::TokenKind);
|
||||
context.emitter().Emit(token, ExpectedDeclName,
|
||||
context.tokens().GetKind(state.token));
|
||||
CARBON_DIAGNOSTIC(
|
||||
ExpectedDeclName, Error,
|
||||
"`{0}` introducer should be followed by a name"
|
||||
"{1:; prefix reserved word with `r#` to form a valid identifier|}",
|
||||
Lex::TokenKind, Diagnostics::BoolAsSelect);
|
||||
context.emitter().Emit(loc, ExpectedDeclName,
|
||||
context.tokens().GetKind(state.token),
|
||||
recover_as_raw);
|
||||
}
|
||||
|
||||
if (recover_as_raw) {
|
||||
HandleName(context, state,
|
||||
context.tokens().AddPostLexingRecoveryTokenAsIdentifier(
|
||||
context.Consume()),
|
||||
NodeKind::IdentifierNameNotBeforeSignature,
|
||||
NodeKind::IdentifierNameQualifierWithoutParams,
|
||||
NodeKind::IdentifierNameMaybeBeforeSignature);
|
||||
return;
|
||||
}
|
||||
context.ReturnErrorOnState();
|
||||
context.AddInvalidParse(*context.position());
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "toolchain/diagnostics/format_providers.h"
|
||||
#include "toolchain/lex/token_kind.h"
|
||||
#include "toolchain/parse/context.h"
|
||||
#include "toolchain/parse/handle.h"
|
||||
@@ -189,7 +190,8 @@ auto HandleExprInPostfix(Context& context) -> void {
|
||||
if (context.PositionKind() != Lex::TokenKind::Period) {
|
||||
CARBON_DIAGNOSTIC(ExpectedPeriodAfterPackage, Error,
|
||||
"expected `.` after `package` expression");
|
||||
context.emitter().Emit(*context.position(), ExpectedPeriodAfterPackage);
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
ExpectedPeriodAfterPackage);
|
||||
state.has_error = true;
|
||||
}
|
||||
context.PushState(state);
|
||||
@@ -228,12 +230,21 @@ auto HandleExprInPostfix(Context& context) -> void {
|
||||
NodeKind::SelfTypeName)) {
|
||||
// OK, `.Self`.
|
||||
} else {
|
||||
CARBON_DIAGNOSTIC(ExpectedIdentifierOrSelfAfterPeriod, Error,
|
||||
"expected identifier or `Self` after `.`");
|
||||
context.emitter().Emit(*context.position(),
|
||||
ExpectedIdentifierOrSelfAfterPeriod);
|
||||
// A keyword after the `.` was most likely meant as the name, and is
|
||||
// what the message is about. Otherwise the name is missing, and goes
|
||||
// after the `.`.
|
||||
bool recover_as_raw = context.PositionKind().is_keyword();
|
||||
CARBON_DIAGNOSTIC(
|
||||
ExpectedIdentifierOrSelfAfterPeriod, Error,
|
||||
"expected identifier or `Self` after `.`"
|
||||
"{0:; prefix reserved word with `r#` to form a valid identifier|}",
|
||||
Diagnostics::BoolAsSelect);
|
||||
context.emitter().Emit(
|
||||
recover_as_raw ? DiagnosticLoc(*context.position())
|
||||
: context.PointAfterPreviousToken(),
|
||||
ExpectedIdentifierOrSelfAfterPeriod, recover_as_raw);
|
||||
// Only consume if it is a number or word.
|
||||
if (context.PositionKind().is_keyword()) {
|
||||
if (recover_as_raw) {
|
||||
context.AddLeafNode(NodeKind::IdentifierNameNotBeforeSignature,
|
||||
context.Consume(), /*has_error=*/true);
|
||||
} else if (context.PositionIs(Lex::TokenKind::IntLiteral)) {
|
||||
@@ -262,7 +273,16 @@ auto HandleExprInPostfix(Context& context) -> void {
|
||||
[&](const Context::State& s) { return s.kind; }));
|
||||
bool in_pattern = state_kinds == llvm::ArrayRef{StateKind::ExprPattern,
|
||||
StateKind::ExprLoop};
|
||||
context.emitter().Emit(*context.position(),
|
||||
// The missing expression goes after the token before it, unless it
|
||||
// starts a statement. Then the token before it ends another statement,
|
||||
// and the statement starts at the token found in the expression's
|
||||
// place.
|
||||
bool starts_statement =
|
||||
state_kinds ==
|
||||
llvm::ArrayRef{StateKind::ExprStatementFinish, StateKind::ExprLoop};
|
||||
context.emitter().Emit(starts_statement
|
||||
? DiagnosticLoc(*context.position())
|
||||
: context.PointAfterPreviousToken(),
|
||||
in_pattern ? ExpectedPattern : ExpectedExpr);
|
||||
}
|
||||
|
||||
@@ -508,7 +528,7 @@ auto HandleExprStatementFinish(Context& context) -> void {
|
||||
if (!state.has_error) {
|
||||
CARBON_DIAGNOSTIC(ExpectedExprSemi, Error,
|
||||
"expected `;` after expression statement");
|
||||
context.emitter().Emit(*context.position(), ExpectedExprSemi);
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(), ExpectedExprSemi);
|
||||
}
|
||||
|
||||
context.AddNode(NodeKind::ExprStatement,
|
||||
|
||||
@@ -40,7 +40,8 @@ auto HandlePrimitiveForm(Context& context) -> void {
|
||||
} else {
|
||||
CARBON_DIAGNOSTIC(ExpectedCategoryModifier, Error,
|
||||
"expected `ref`, `var`, or `val` after `form(`");
|
||||
context.emitter().Emit(*context.position(), ExpectedCategoryModifier);
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
ExpectedCategoryModifier);
|
||||
state.has_error = true;
|
||||
}
|
||||
context.PushState(state, StateKind::PrimitiveFormFinish);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "toolchain/diagnostics/format_providers.h"
|
||||
#include "toolchain/parse/context.h"
|
||||
#include "toolchain/parse/handle.h"
|
||||
|
||||
@@ -18,13 +19,29 @@ auto HandleFriendDecl(Context& context) -> void {
|
||||
|
||||
if (!context.ConsumeAndAddLeafNodeIf(Lex::TokenKind::Identifier,
|
||||
NodeKind::IdentifierNameExpr)) {
|
||||
state.has_error = true;
|
||||
CARBON_DIAGNOSTIC(ExpectedIdentifierForFriend, Error,
|
||||
"expected identifier for friend declaration");
|
||||
context.emitter().Emit(*context.position(), ExpectedIdentifierForFriend);
|
||||
context.RecoverFromDeclError(state, NodeKind::FriendDecl,
|
||||
/*skip_past_likely_end=*/true);
|
||||
return;
|
||||
// A word that can't be a name, followed by the `;`, was most likely meant
|
||||
// as the name, and is what the message is about. Otherwise the name is
|
||||
// missing, and goes after `friend`.
|
||||
bool recover_as_raw =
|
||||
context.PositionKind().is_word() &&
|
||||
context.PositionKind(Lookahead::NextToken) == Lex::TokenKind::Semi;
|
||||
CARBON_DIAGNOSTIC(
|
||||
ExpectedIdentifierForFriend, Error,
|
||||
"expected identifier for friend declaration"
|
||||
"{0:; prefix reserved word with `r#` to form a valid identifier|}",
|
||||
Diagnostics::BoolAsSelect);
|
||||
context.emitter().Emit(recover_as_raw ? DiagnosticLoc(*context.position())
|
||||
: context.PointAfterPreviousToken(),
|
||||
ExpectedIdentifierForFriend, recover_as_raw);
|
||||
if (!recover_as_raw) {
|
||||
state.has_error = true;
|
||||
context.RecoverFromDeclError(state, NodeKind::FriendDecl,
|
||||
/*skip_past_likely_end=*/true);
|
||||
return;
|
||||
}
|
||||
context.AddLeafNode(NodeKind::IdentifierNameExpr,
|
||||
context.tokens().AddPostLexingRecoveryTokenAsIdentifier(
|
||||
context.Consume()));
|
||||
}
|
||||
context.AddNodeExpectingDeclSemi(state, NodeKind::FriendDecl,
|
||||
Lex::TokenKind::Friend,
|
||||
|
||||
@@ -89,7 +89,8 @@ auto HandleFunctionSignatureFinish(Context& context) -> void {
|
||||
NodeKind::BuiltinName)) {
|
||||
CARBON_DIAGNOSTIC(ExpectedBuiltinName, Error,
|
||||
"expected builtin function name after `=`");
|
||||
context.emitter().Emit(*context.position(), ExpectedBuiltinName);
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
ExpectedBuiltinName);
|
||||
state.has_error = true;
|
||||
}
|
||||
auto semi = context.ConsumeIf(Lex::TokenKind::Semi);
|
||||
|
||||
@@ -22,7 +22,8 @@ auto HandleIfExprFinishCondition(Context& context) -> void {
|
||||
CARBON_DIAGNOSTIC(ExpectedThenAfterIf, Error,
|
||||
"expected `then` after `if` condition");
|
||||
if (!state.has_error) {
|
||||
context.emitter().Emit(*context.position(), ExpectedThenAfterIf);
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
ExpectedThenAfterIf);
|
||||
}
|
||||
// Add invalid nodes to substitute for `IfExprThen` and the final `Expr`.
|
||||
context.AddInvalidParse(*context.position());
|
||||
@@ -45,7 +46,8 @@ auto HandleIfExprFinishThen(Context& context) -> void {
|
||||
CARBON_DIAGNOSTIC(ExpectedElseAfterIf, Error,
|
||||
"expected `else` after `if ... then ...`");
|
||||
if (!state.has_error) {
|
||||
context.emitter().Emit(*context.position(), ExpectedElseAfterIf);
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
ExpectedElseAfterIf);
|
||||
}
|
||||
// Add an invalid node to substitute for the final `Expr`.
|
||||
context.AddInvalidParse(*context.position());
|
||||
|
||||
@@ -34,7 +34,8 @@ auto HandleImplAfterIntroducer(Context& context) -> void {
|
||||
} else {
|
||||
CARBON_DIAGNOSTIC(ImplExpectedAfterForall, Error,
|
||||
"expected `[` after `forall` in `impl` declaration");
|
||||
context.emitter().Emit(*context.position(), ImplExpectedAfterForall);
|
||||
context.emitter().Emit(context.OpeningBracketInsertionPoint(),
|
||||
ImplExpectedAfterForall);
|
||||
context.ReturnErrorOnState();
|
||||
// If we aren't producing a node from the PatternListAsImplicit state,
|
||||
// we still need to create a node to be the child of the `ImplForall`
|
||||
@@ -69,7 +70,7 @@ auto HandleImplBeforeAs(Context& context) -> void {
|
||||
if (!state.has_error) {
|
||||
CARBON_DIAGNOSTIC(ImplExpectedAs, Error,
|
||||
"expected `as` in `impl` declaration");
|
||||
context.emitter().Emit(*context.position(), ImplExpectedAs);
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(), ImplExpectedAs);
|
||||
}
|
||||
context.ReturnErrorOnState();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "toolchain/base/shared_value_stores.h"
|
||||
#include "toolchain/diagnostics/format_providers.h"
|
||||
#include "toolchain/lex/token_kind.h"
|
||||
#include "toolchain/lex/tokenized_buffer.h"
|
||||
#include "toolchain/parse/context.h"
|
||||
@@ -19,27 +20,27 @@ static auto OnParseError(Context& context, Context::State state,
|
||||
/*has_error=*/true);
|
||||
}
|
||||
|
||||
// Determines whether the specified modifier appears within the introducer of
|
||||
// the given declaration.
|
||||
// Finds the specified modifier within the introducer of the given declaration,
|
||||
// returning `None` when it isn't there.
|
||||
// TODO: Restructure how we handle packaging declarations to avoid the need to
|
||||
// do this.
|
||||
static auto HasModifier(Context& context, Context::State state,
|
||||
Lex::TokenKind modifier) -> bool {
|
||||
static auto FindModifier(Context& context, Context::State state,
|
||||
Lex::TokenKind modifier) -> Lex::TokenIndex {
|
||||
for (Lex::TokenIterator it(state.token); it != context.position(); ++it) {
|
||||
if (context.tokens().GetKind(*it) == modifier) {
|
||||
return true;
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return Lex::TokenIndex::None;
|
||||
}
|
||||
|
||||
// Handles everything after the declaration's introducer.
|
||||
template <const Parse::NodeKind& DeclKind>
|
||||
static auto HandleDeclContent(Context& context, Context::State state,
|
||||
bool is_export, bool is_impl,
|
||||
Lex::TokenIndex export_token, bool is_impl,
|
||||
llvm::function_ref<auto()->void> on_parse_error)
|
||||
-> void {
|
||||
Tree::PackagingNames names = {.is_export = is_export};
|
||||
Tree::PackagingNames names = {.is_export = export_token.has_value()};
|
||||
|
||||
// Parse the package name.
|
||||
if (DeclKind == NodeKind::LibraryDecl ||
|
||||
@@ -50,7 +51,6 @@ static auto HandleDeclContent(Context& context, Context::State state,
|
||||
} else {
|
||||
// We require a package name. This is either an identifier or package name
|
||||
// keyword.
|
||||
auto package_name_position = *context.position();
|
||||
if (auto ident = context.ConsumeIf(Lex::TokenKind::Identifier)) {
|
||||
names.package_id =
|
||||
PackageNameId::ForIdentifier(context.tokens().GetIdentifier(*ident));
|
||||
@@ -62,16 +62,38 @@ static auto HandleDeclContent(Context& context, Context::State state,
|
||||
names.package_id = PackageNameId::Cpp;
|
||||
context.AddLeafNode(NodeKind::CppPackageName, *cpp);
|
||||
} else {
|
||||
CARBON_DIAGNOSTIC(ExpectedIdentifierAfterPackage, Error,
|
||||
"expected identifier after `package`");
|
||||
CARBON_DIAGNOSTIC(ExpectedIdentifierAfterImport, Error,
|
||||
"expected identifier or `library` after `import`");
|
||||
context.emitter().Emit(package_name_position,
|
||||
// A word that can't be a name, followed by a token that can follow one,
|
||||
// was most likely meant as the name, and is what the message is about.
|
||||
// Otherwise the name is missing, and goes after the token before it.
|
||||
bool recover_as_raw =
|
||||
context.PositionKind().is_word() &&
|
||||
context.PositionKind(Lookahead::NextToken)
|
||||
.IsOneOf({Lex::TokenKind::Semi, Lex::TokenKind::Library});
|
||||
CARBON_DIAGNOSTIC(
|
||||
ExpectedIdentifierAfterPackage, Error,
|
||||
"expected identifier after `package`"
|
||||
"{0:; prefix reserved word with `r#` to form a valid identifier|}",
|
||||
Diagnostics::BoolAsSelect);
|
||||
CARBON_DIAGNOSTIC(
|
||||
ExpectedIdentifierAfterImport, Error,
|
||||
"expected identifier or `library` after `import`"
|
||||
"{0:; prefix reserved word with `r#` to form a valid identifier|}",
|
||||
Diagnostics::BoolAsSelect);
|
||||
context.emitter().Emit(recover_as_raw ? DiagnosticLoc(*context.position())
|
||||
: context.PointAfterPreviousToken(),
|
||||
DeclKind == NodeKind::PackageDecl
|
||||
? ExpectedIdentifierAfterPackage
|
||||
: ExpectedIdentifierAfterImport);
|
||||
on_parse_error();
|
||||
return;
|
||||
: ExpectedIdentifierAfterImport,
|
||||
recover_as_raw);
|
||||
if (!recover_as_raw) {
|
||||
on_parse_error();
|
||||
return;
|
||||
}
|
||||
auto ident = context.tokens().AddPostLexingRecoveryTokenAsIdentifier(
|
||||
context.Consume());
|
||||
names.package_id =
|
||||
PackageNameId::ForIdentifier(context.tokens().GetIdentifier(ident));
|
||||
context.AddLeafNode(NodeKind::IdentifierPackageName, ident);
|
||||
}
|
||||
|
||||
if (names.is_export) {
|
||||
@@ -80,7 +102,7 @@ static auto HandleDeclContent(Context& context, Context::State state,
|
||||
|
||||
CARBON_DIAGNOSTIC(ExportImportPackage, Error,
|
||||
"`export` cannot be used when importing a package");
|
||||
context.emitter().Emit(package_name_position, ExportImportPackage);
|
||||
context.emitter().Emit(export_token, ExportImportPackage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +136,8 @@ static auto HandleDeclContent(Context& context, Context::State state,
|
||||
if (!inline_body_token) {
|
||||
CARBON_DIAGNOSTIC(ExpectedStringAfterInline, Error,
|
||||
"expected string literal after `inline`");
|
||||
context.emitter().Emit(body_position, ExpectedStringAfterInline);
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
ExpectedStringAfterInline);
|
||||
on_parse_error();
|
||||
return;
|
||||
}
|
||||
@@ -125,7 +148,8 @@ static auto HandleDeclContent(Context& context, Context::State state,
|
||||
// before the library name.
|
||||
CARBON_DIAGNOSTIC(MissingLibraryKeyword, Error,
|
||||
"missing `library` keyword");
|
||||
context.emitter().Emit(*context.position(), MissingLibraryKeyword);
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
MissingLibraryKeyword);
|
||||
on_parse_error();
|
||||
return;
|
||||
}
|
||||
@@ -203,12 +227,12 @@ auto HandleImport(Context& context) -> void {
|
||||
|
||||
if (VerifyInImports(context, state.token)) {
|
||||
// Scan the modifiers to see if this import declaration is exported.
|
||||
bool is_export = HasModifier(context, state, Lex::TokenKind::Export);
|
||||
if (is_export) {
|
||||
auto export_token = FindModifier(context, state, Lex::TokenKind::Export);
|
||||
if (export_token.has_value()) {
|
||||
RestrictExportToApi(context, state);
|
||||
}
|
||||
|
||||
HandleDeclContent<NodeKind::ImportDecl>(context, state, is_export,
|
||||
HandleDeclContent<NodeKind::ImportDecl>(context, state, export_token,
|
||||
/*is_impl=*/false, on_parse_error);
|
||||
} else {
|
||||
on_parse_error();
|
||||
@@ -240,7 +264,7 @@ static auto HandlePackageAndLibraryDecls(Context& context,
|
||||
-> void {
|
||||
auto state = context.PopState();
|
||||
|
||||
bool is_impl = HasModifier(context, state, Lex::TokenKind::Impl);
|
||||
bool is_impl = FindModifier(context, state, Lex::TokenKind::Impl).has_value();
|
||||
|
||||
auto on_parse_error = [&] { OnParseError(context, state, DeclKind); };
|
||||
|
||||
@@ -262,7 +286,8 @@ static auto HandlePackageAndLibraryDecls(Context& context,
|
||||
// `package`/`library` is no longer allowed, but `import` may repeat.
|
||||
context.set_packaging_state(Context::PackagingState::InImports);
|
||||
|
||||
HandleDeclContent<DeclKind>(context, state, /*is_export=*/false, is_impl,
|
||||
HandleDeclContent<DeclKind>(context, state,
|
||||
/*export_token=*/Lex::TokenIndex::None, is_impl,
|
||||
on_parse_error);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@ auto HandleInlineDeclAfterIntroducer(Context& context) -> void {
|
||||
} else {
|
||||
CARBON_DIAGNOSTIC(ExpectedCppAfterInline, Error,
|
||||
"expected `Cpp` after `inline`");
|
||||
context.emitter().Emit(*context.position(), ExpectedCppAfterInline);
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
ExpectedCppAfterInline);
|
||||
context.AddNode(NodeKind::InlineCppDecl,
|
||||
context.SkipPastLikelyEnd(state.token),
|
||||
/*has_error=*/true);
|
||||
@@ -33,7 +34,8 @@ auto HandleInlineDeclAfterIntroducer(Context& context) -> void {
|
||||
} else {
|
||||
CARBON_DIAGNOSTIC(ExpectedStringAfterInlineCpp, Error,
|
||||
"expected string literal after `inline Cpp`");
|
||||
context.emitter().Emit(*context.position(), ExpectedStringAfterInlineCpp);
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
ExpectedStringAfterInlineCpp);
|
||||
context.AddNode(NodeKind::InlineCppDecl,
|
||||
context.SkipPastLikelyEnd(state.token),
|
||||
/*has_error=*/true);
|
||||
|
||||
@@ -60,12 +60,13 @@ static auto ParseLambdaBody(Context& context, Context::State state,
|
||||
if (has_return_type) {
|
||||
CARBON_DIAGNOSTIC(ExpectedLambdaBodyAfterReturnType, Error,
|
||||
"expected `{{` after return type");
|
||||
context.emitter().Emit(*context.position(),
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
ExpectedLambdaBodyAfterReturnType);
|
||||
} else {
|
||||
CARBON_DIAGNOSTIC(ExpectedLambdaBody, Error,
|
||||
"expected `->`, `=>`, or `{{`");
|
||||
context.emitter().Emit(*context.position(), ExpectedLambdaBody);
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
ExpectedLambdaBody);
|
||||
}
|
||||
|
||||
// Bundle everything into a complete lambda node for error recovery --
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "toolchain/diagnostics/diagnostic.h"
|
||||
#include "toolchain/diagnostics/format_providers.h"
|
||||
#include "toolchain/lex/token_kind.h"
|
||||
#include "toolchain/parse/context.h"
|
||||
#include "toolchain/parse/handle.h"
|
||||
@@ -41,21 +42,37 @@ auto HandleAssociatedConstant(Context& context) -> void {
|
||||
"phase keyword is not allowed on an associated constant");
|
||||
context.emitter().Emit(*context.position(),
|
||||
PhaseKeywordInAssociatedConstant);
|
||||
state.has_error = true;
|
||||
} else {
|
||||
// A word that can't be a name, followed by the `:`, was most likely
|
||||
// meant as the name, and is what the message is about. Otherwise the
|
||||
// name is missing, and goes after `let`.
|
||||
bool recover_as_raw =
|
||||
kind.is_word() &&
|
||||
context.PositionKind(Lookahead::NextToken) == Lex::TokenKind::Colon;
|
||||
CARBON_DIAGNOSTIC(
|
||||
ExpectedAssociatedConstantIdentifier, Error,
|
||||
"expected identifier in associated constant declaration");
|
||||
context.emitter().Emit(*context.position(),
|
||||
ExpectedAssociatedConstantIdentifier);
|
||||
"expected identifier in associated constant declaration"
|
||||
"{0:; prefix reserved word with `r#` to form a valid identifier|}",
|
||||
Diagnostics::BoolAsSelect);
|
||||
context.emitter().Emit(recover_as_raw ? DiagnosticLoc(*context.position())
|
||||
: context.PointAfterPreviousToken(),
|
||||
ExpectedAssociatedConstantIdentifier,
|
||||
recover_as_raw);
|
||||
if (recover_as_raw) {
|
||||
identifier = context.tokens().AddPostLexingRecoveryTokenAsIdentifier(
|
||||
context.Consume());
|
||||
} else {
|
||||
state.has_error = true;
|
||||
}
|
||||
}
|
||||
state.has_error = true;
|
||||
}
|
||||
|
||||
auto colon = context.ConsumeIf(Lex::TokenKind::Colon);
|
||||
if (identifier && !colon) {
|
||||
CARBON_DIAGNOSTIC(ExpectedAssociatedConstantColon, Error,
|
||||
"expected `:` in associated constant declaration");
|
||||
context.emitter().Emit(*context.position(),
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
ExpectedAssociatedConstantColon);
|
||||
state.has_error = true;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ static auto HandleMatchHandlerStart(Context& context, NodeKind label_kind)
|
||||
if (!state.has_error) {
|
||||
CARBON_DIAGNOSTIC(ExpectedMatchCaseArrow, Error,
|
||||
"expected `=>` introducing statement block");
|
||||
context.emitter().Emit(*context.position(), ExpectedMatchCaseArrow);
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
ExpectedMatchCaseArrow);
|
||||
}
|
||||
|
||||
context.AddNode(label_kind, *context.position(), /*has_error=*/true);
|
||||
@@ -33,7 +34,8 @@ static auto HandleMatchHandlerStart(Context& context, NodeKind label_kind)
|
||||
if (!state.has_error) {
|
||||
CARBON_DIAGNOSTIC(ExpectedMatchCaseBlock, Error,
|
||||
"expected `{{` after `=>`");
|
||||
context.emitter().Emit(*context.position(), ExpectedMatchCaseBlock);
|
||||
context.emitter().Emit(context.OpeningBracketInsertionPoint(),
|
||||
ExpectedMatchCaseBlock);
|
||||
}
|
||||
|
||||
context.AddNode(NodeKind::MatchHandlerStart, *context.position(),
|
||||
@@ -77,7 +79,8 @@ auto HandleMatchConditionFinish(Context& context) -> void {
|
||||
if (!state.has_error) {
|
||||
CARBON_DIAGNOSTIC(ExpectedMatchCasesBlock, Error,
|
||||
"expected `{{` starting block with cases");
|
||||
context.emitter().Emit(*context.position(), ExpectedMatchCasesBlock);
|
||||
context.emitter().Emit(context.OpeningBracketInsertionPoint(),
|
||||
ExpectedMatchCasesBlock);
|
||||
}
|
||||
|
||||
context.AddNode(NodeKind::MatchStatementStart, *context.position(),
|
||||
@@ -94,7 +97,8 @@ auto HandleMatchConditionFinish(Context& context) -> void {
|
||||
state.has_error = false;
|
||||
if (context.PositionIs(Lex::TokenKind::CloseCurlyBrace)) {
|
||||
CARBON_DIAGNOSTIC(ExpectedMatchCases, Error, "expected cases");
|
||||
context.emitter().Emit(*context.position(), ExpectedMatchCases);
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
ExpectedMatchCases);
|
||||
state.has_error = true;
|
||||
}
|
||||
|
||||
@@ -174,7 +178,7 @@ auto HandleMatchCaseAfterPattern(Context& context) -> void {
|
||||
if (!state.has_error) {
|
||||
CARBON_DIAGNOSTIC(ExpectedMatchCaseGuardOpenParen, Error,
|
||||
"expected `(` after `if`");
|
||||
context.emitter().Emit(*context.position(),
|
||||
context.emitter().Emit(context.OpeningBracketInsertionPoint(),
|
||||
ExpectedMatchCaseGuardOpenParen);
|
||||
}
|
||||
|
||||
@@ -207,7 +211,7 @@ auto HandleMatchCaseGuardFinish(Context& context) -> void {
|
||||
if (!state.has_error) {
|
||||
CARBON_DIAGNOSTIC(ExpectedMatchCaseGuardCloseParen, Error,
|
||||
"expected `)`");
|
||||
context.emitter().Emit(*context.position(),
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
ExpectedMatchCaseGuardCloseParen);
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,8 @@ auto HandleObserveOperator(Context& context) -> void {
|
||||
if (!state.has_error) {
|
||||
CARBON_DIAGNOSTIC(ExpectedObserveOperator, Error,
|
||||
"observe should use `==` or `impls` operator");
|
||||
context.emitter().Emit(*context.position(), ExpectedObserveOperator);
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
ExpectedObserveOperator);
|
||||
}
|
||||
context.RecoverFromDeclError(state, NodeKind::ObserveDecl,
|
||||
/*skip_past_likely_end=*/true);
|
||||
|
||||
@@ -31,7 +31,7 @@ auto HandleOnlyParenExprFinish(Context& context) -> void {
|
||||
if (!state.has_error) {
|
||||
CARBON_DIAGNOSTIC(UnexpectedTokenInCompoundMemberAccess, Error,
|
||||
"expected `)`");
|
||||
context.emitter().Emit(*context.position(),
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
UnexpectedTokenInCompoundMemberAccess);
|
||||
state.has_error = true;
|
||||
}
|
||||
|
||||
@@ -76,11 +76,12 @@ auto HandleExprPattern(Context& context) -> void {
|
||||
if (context.PositionKind().is_binding_pattern_operator()) {
|
||||
if (!state.has_error) {
|
||||
CARBON_DIAGNOSTIC(ExpectedBindingName, Error,
|
||||
"unexpected expression before {0} in binding pattern",
|
||||
"unexpected expression before `{0}` in binding pattern",
|
||||
Lex::TokenKind);
|
||||
// TODO: Underline the parsed expression.
|
||||
context.emitter().Emit(*context.position(), ExpectedBindingName,
|
||||
context.PositionKind());
|
||||
context.emitter().Emit(
|
||||
DiagnosticLoc::Tokens(
|
||||
{.begin = state.token, .end = *(context.position() - 1)}),
|
||||
ExpectedBindingName, context.PositionKind());
|
||||
state.has_error = true;
|
||||
}
|
||||
context.Consume();
|
||||
|
||||
@@ -107,7 +107,7 @@ auto HandleStructPatternFieldAfterDesignator(Context& context) -> void {
|
||||
|
||||
CARBON_DIAGNOSTIC(ExpectedStructPatternDesignatedField, Error,
|
||||
"expected `= value` after `.field`");
|
||||
context.emitter().Emit(*context.position(),
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
ExpectedStructPatternDesignatedField);
|
||||
}
|
||||
skip_to_recovery_position(/*add_invalid_parse=*/true);
|
||||
|
||||
@@ -44,7 +44,10 @@ static auto HandlePeriodOrArrow(Context& context, NodeKind node_kind,
|
||||
"expected identifier after `{0:->|.}`"
|
||||
"{1:; prefix reserved word with `r#` to form a valid identifier|}",
|
||||
Diagnostics::BoolAsSelect, Diagnostics::BoolAsSelect);
|
||||
context.emitter().Emit(*context.position(),
|
||||
// A reserved word in the name's position is what the message is about.
|
||||
// Otherwise the name is missing, and goes after the `.` or `->`.
|
||||
context.emitter().Emit(recover_as_raw ? DiagnosticLoc(*context.position())
|
||||
: context.PointAfterPreviousToken(),
|
||||
ExpectedIdentifierAfterPeriodOrArrow, is_arrow,
|
||||
recover_as_raw);
|
||||
// If we see a word, assume it was intended to be a name.
|
||||
|
||||
@@ -32,7 +32,8 @@ auto HandleRequireBeforeImpls(Context& context) -> void {
|
||||
if (!state.has_error) {
|
||||
CARBON_DIAGNOSTIC(RequireExpectedImpls, Error,
|
||||
"expected `impls` in `require` declaration");
|
||||
context.emitter().Emit(*context.position(), RequireExpectedImpls);
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
RequireExpectedImpls);
|
||||
}
|
||||
context.ReturnErrorOnState();
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ auto HandleRequirementOperator(Context& context) -> void {
|
||||
CARBON_DIAGNOSTIC(
|
||||
ExpectedRequirementOperator, Error,
|
||||
"requirement should use `impls`, `=`, or `==` operator");
|
||||
context.emitter().Emit(*context.position(),
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
ExpectedRequirementOperator);
|
||||
}
|
||||
context.ReturnErrorOnState();
|
||||
|
||||
@@ -96,7 +96,8 @@ static auto HandleStatementKeywordFinish(Context& context, NodeKind node_kind)
|
||||
if (!semi) {
|
||||
CARBON_DIAGNOSTIC(ExpectedStatementSemi, Error,
|
||||
"`{0}` statements must end with a `;`", Lex::TokenKind);
|
||||
context.emitter().Emit(*context.position(), ExpectedStatementSemi,
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
ExpectedStatementSemi,
|
||||
context.tokens().GetKind(state.token));
|
||||
state.has_error = true;
|
||||
// Recover to the next semicolon if possible.
|
||||
@@ -144,7 +145,7 @@ auto HandleStatementForHeaderIn(Context& context) -> void {
|
||||
end_token = context.Consume();
|
||||
} else if (!state.has_error) {
|
||||
CARBON_DIAGNOSTIC(ExpectedIn, Error, "expected `in` after loop pattern");
|
||||
context.emitter().Emit(*context.position(), ExpectedIn);
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(), ExpectedIn);
|
||||
state.has_error = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,8 @@ auto HandleVarAsReturned(Context& context) -> void {
|
||||
if (!context.PositionIs(Lex::TokenKind::Var)) {
|
||||
CARBON_DIAGNOSTIC(ExpectedVarAfterReturned, Error,
|
||||
"expected `var` after `returned`");
|
||||
context.emitter().Emit(*context.position(), ExpectedVarAfterReturned);
|
||||
context.emitter().Emit(context.PointAfterPreviousToken(),
|
||||
ExpectedVarAfterReturned);
|
||||
context.AddLeafNode(NodeKind::EmptyDecl,
|
||||
context.SkipPastLikelyEnd(returned_token),
|
||||
/*has_error=*/true);
|
||||
|
||||
+9
-9
@@ -8,11 +8,11 @@
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/alias/fail_syntax.carbon
|
||||
|
||||
// CHECK:STDERR: fail_syntax.carbon:[[@LINE+2]]:7: error: `alias` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR: fail_syntax.carbon:[[@LINE+2]]:6: error: `alias` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR:
|
||||
alias = b;
|
||||
|
||||
// CHECK:STDERR: fail_syntax.carbon:[[@LINE+2]]:9: error: `alias` requires a `=` for the source [ExpectedAliasInitializer]
|
||||
// CHECK:STDERR: fail_syntax.carbon:[[@LINE+2]]:8: error: `alias` requires a `=` for the source [ExpectedAliasInitializer]
|
||||
// CHECK:STDERR:
|
||||
alias a b;
|
||||
|
||||
@@ -24,15 +24,15 @@ alias a =;
|
||||
// CHECK:STDERR:
|
||||
alias a;
|
||||
|
||||
// CHECK:STDERR: fail_syntax.carbon:[[@LINE+2]]:7: error: `alias` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR: fail_syntax.carbon:[[@LINE+2]]:6: error: `alias` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR:
|
||||
alias =;
|
||||
|
||||
// CHECK:STDERR: fail_syntax.carbon:[[@LINE+2]]:9: error: `alias` requires a `=` for the source [ExpectedAliasInitializer]
|
||||
// CHECK:STDERR: fail_syntax.carbon:[[@LINE+2]]:8: error: `alias` requires a `=` for the source [ExpectedAliasInitializer]
|
||||
// CHECK:STDERR:
|
||||
alias a b c = d;
|
||||
|
||||
// CHECK:STDERR: fail_syntax.carbon:[[@LINE+2]]:13: error: `alias` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR: fail_syntax.carbon:[[@LINE+2]]:12: error: `alias` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR:
|
||||
alias a = b c d;
|
||||
|
||||
@@ -45,15 +45,15 @@ fn F() {
|
||||
// CHECK:STDERR:
|
||||
alias a =;
|
||||
|
||||
// CHECK:STDERR: fail_syntax.carbon:[[@LINE+2]]:12: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
alias a =
|
||||
// CHECK:STDERR: fail_syntax.carbon:[[@LINE+2]]:1: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
}
|
||||
|
||||
// CHECK:STDERR: fail_syntax.carbon:[[@LINE+2]]:12: error: `alias` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR:
|
||||
alias a = b
|
||||
|
||||
// CHECK:STDERR: fail_syntax.carbon:[[@LINE+54]]:26: error: `alias` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDOUT: - filename: fail_syntax.carbon
|
||||
// CHECK:STDOUT: ╭─FileStart ''
|
||||
// CHECK:STDOUT: │ ╭─AliasIntroducer 'alias'
|
||||
|
||||
+10
-8
@@ -18,29 +18,31 @@ var x: array(i32);
|
||||
|
||||
// --- fail_invalid_char.carbon
|
||||
|
||||
// CHECK:STDERR: fail_invalid_char.carbon:[[@LINE+8]]:17: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR: fail_invalid_char.carbon:[[@LINE+9]]:17: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_invalid_char.carbon:[[@LINE+6]]:17: error: expected `,` in `array(Type, Count)` [ExpectedArrayComma]
|
||||
// CHECK:STDERR: fail_invalid_char.carbon:[[@LINE+7]]:17: error: expected `,` in `array(Type, Count)` [ExpectedArrayComma]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_invalid_char.carbon:[[@LINE+4]]:17: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR: fail_invalid_char.carbon:[[@LINE+5]]:17: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_invalid_char.carbon:[[@LINE+2]]:17: error: unexpected tokens before `)` [ExpectedCloseSymbol]
|
||||
// CHECK:STDERR: fail_invalid_char.carbon:[[@LINE+3]]:17: error: unexpected tokens before `)` [ExpectedCloseSymbol]
|
||||
// CHECK:STDERR: fail_invalid_char.carbon:[[@LINE+2]]:16: note: matching this `(` [GroupOpenedHere]
|
||||
// CHECK:STDERR:
|
||||
fn X() -> array(:);
|
||||
|
||||
// --- fail_unlexed_expr.carbon
|
||||
|
||||
// CHECK:STDERR: fail_unlexed_expr.carbon:[[@LINE+6]]:14: error: encountered unrecognized characters while parsing [UnrecognizedCharacters]
|
||||
// CHECK:STDERR: fail_unlexed_expr.carbon:[[@LINE+7]]:14: error: encountered unrecognized characters while parsing [UnrecognizedCharacters]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_unlexed_expr.carbon:[[@LINE+4]]:14: error: expected `,` in `array(Type, Count)` [ExpectedArrayComma]
|
||||
// CHECK:STDERR: fail_unlexed_expr.carbon:[[@LINE+5]]:14: error: expected `,` in `array(Type, Count)` [ExpectedArrayComma]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_unlexed_expr.carbon:[[@LINE+2]]:14: error: unexpected tokens before `)` [ExpectedCloseSymbol]
|
||||
// CHECK:STDERR: fail_unlexed_expr.carbon:[[@LINE+3]]:14: error: unexpected tokens before `)` [ExpectedCloseSymbol]
|
||||
// CHECK:STDERR: fail_unlexed_expr.carbon:[[@LINE+2]]:13: note: matching this `(` [GroupOpenedHere]
|
||||
// CHECK:STDERR:
|
||||
var y: array(`);
|
||||
|
||||
// --- fail_no_open_paren.carbon
|
||||
|
||||
// CHECK:STDERR: fail_no_open_paren.carbon:[[@LINE+2]]:14-16: error: expected `(` after `array` [ExpectedParenAfter]
|
||||
// CHECK:STDERR: fail_no_open_paren.carbon:[[@LINE+2]]:14: error: expected `(` after `array` [ExpectedParenAfter]
|
||||
// CHECK:STDERR:
|
||||
var x: array i32, 1;
|
||||
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/basics/fail_missing_token_locations.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/basics/fail_missing_token_locations.carbon
|
||||
|
||||
// --- fail_semi_before_comment.carbon
|
||||
|
||||
// The `;` goes after the `1`, before the spaces and the comment.
|
||||
// CHECK:STDERR: fail_semi_before_comment.carbon:[[@LINE+2]]:15: error: `var` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR:
|
||||
var x: i32 = 1 // The value.
|
||||
var y: i32 = 2;
|
||||
|
||||
// --- fail_expr_at_line_end.carbon
|
||||
|
||||
fn F() {
|
||||
// CHECK:STDERR: fail_expr_at_line_end.carbon:[[@LINE+4]]:15: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_expr_at_line_end.carbon:[[@LINE+2]]:15: error: `var` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR:
|
||||
var x: i32 =
|
||||
var y: i32 = 2;
|
||||
}
|
||||
|
||||
// --- fail_expr_at_statement_start.carbon
|
||||
|
||||
// The statement starts at the `=`, so the missing expression goes there rather
|
||||
// than at the end of the line before.
|
||||
fn F() {
|
||||
var x: i32 = 1;
|
||||
// CHECK:STDERR: fail_expr_at_statement_start.carbon:[[@LINE+2]]:3: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
= 3;
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: - filename: fail_semi_before_comment.carbon
|
||||
// CHECK:STDOUT: ╭─FileStart ''
|
||||
// CHECK:STDOUT: │ ╭─VariableIntroducer 'var'
|
||||
// CHECK:STDOUT: │ │ ╭─IdentifierNameNotBeforeSignature 'x'
|
||||
// CHECK:STDOUT: │ │ ├─BindingPatternTypeStart ':'
|
||||
// CHECK:STDOUT: │ │ ├─IntTypeLiteral 'i32'
|
||||
// CHECK:STDOUT: │ │ ╭─VarBindingPattern ':'
|
||||
// CHECK:STDOUT: │ ├─VariablePattern 'var'
|
||||
// CHECK:STDOUT: │ ├─VariableInitializer '='
|
||||
// CHECK:STDOUT: │ ├─IntLiteral '1'
|
||||
// CHECK:STDOUT: ├─VariableDecl ';' has_error
|
||||
// CHECK:STDOUT: ├─FileEnd ''
|
||||
// CHECK:STDOUT: (root)
|
||||
// CHECK:STDOUT: - filename: fail_expr_at_line_end.carbon
|
||||
// CHECK:STDOUT: ╭─FileStart ''
|
||||
// CHECK:STDOUT: │ ╭─FunctionIntroducer 'fn'
|
||||
// CHECK:STDOUT: │ ├─IdentifierNameMaybeBeforeSignature 'F'
|
||||
// CHECK:STDOUT: │ │ ╭─ExplicitParamListStart '('
|
||||
// CHECK:STDOUT: │ ├─ExplicitParamList ')'
|
||||
// CHECK:STDOUT: │ ╭─FunctionDefinitionStart '{'
|
||||
// CHECK:STDOUT: │ │ ╭─VariableIntroducer 'var'
|
||||
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'x'
|
||||
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
|
||||
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
|
||||
// CHECK:STDOUT: │ │ │ ╭─VarBindingPattern ':'
|
||||
// CHECK:STDOUT: │ │ ├─VariablePattern 'var'
|
||||
// CHECK:STDOUT: │ │ ├─VariableInitializer '='
|
||||
// CHECK:STDOUT: │ │ ├─InvalidParse 'var' has_error
|
||||
// CHECK:STDOUT: │ ├─VariableDecl ';' has_error
|
||||
// CHECK:STDOUT: ├─FunctionDefinition '}'
|
||||
// CHECK:STDOUT: ├─FileEnd ''
|
||||
// CHECK:STDOUT: (root)
|
||||
// CHECK:STDOUT: - filename: fail_expr_at_statement_start.carbon
|
||||
// CHECK:STDOUT: ╭─FileStart ''
|
||||
// CHECK:STDOUT: │ ╭─FunctionIntroducer 'fn'
|
||||
// CHECK:STDOUT: │ ├─IdentifierNameMaybeBeforeSignature 'F'
|
||||
// CHECK:STDOUT: │ │ ╭─ExplicitParamListStart '('
|
||||
// CHECK:STDOUT: │ ├─ExplicitParamList ')'
|
||||
// CHECK:STDOUT: │ ╭─FunctionDefinitionStart '{'
|
||||
// CHECK:STDOUT: │ │ ╭─VariableIntroducer 'var'
|
||||
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'x'
|
||||
// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':'
|
||||
// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32'
|
||||
// CHECK:STDOUT: │ │ │ ╭─VarBindingPattern ':'
|
||||
// CHECK:STDOUT: │ │ ├─VariablePattern 'var'
|
||||
// CHECK:STDOUT: │ │ ├─VariableInitializer '='
|
||||
// CHECK:STDOUT: │ │ ├─IntLiteral '1'
|
||||
// CHECK:STDOUT: │ ├─VariableDecl ';'
|
||||
// CHECK:STDOUT: │ │ ╭─InvalidParse '=' has_error
|
||||
// CHECK:STDOUT: │ │ ├─IntLiteral '3'
|
||||
// CHECK:STDOUT: │ │ ╭─InfixOperatorEqual '=' has_error
|
||||
// CHECK:STDOUT: │ ├─ExprStatement ';'
|
||||
// CHECK:STDOUT: ├─FunctionDefinition '}'
|
||||
// CHECK:STDOUT: ├─FileEnd ''
|
||||
// CHECK:STDOUT: (root)
|
||||
@@ -8,14 +8,14 @@
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/basics/fail_paren_match_regression.carbon
|
||||
|
||||
// CHECK:STDERR: fail_paren_match_regression.carbon:[[@LINE+4]]:5: error: expected pattern [ExpectedPattern]
|
||||
// CHECK:STDERR: fail_paren_match_regression.carbon:[[@LINE+6]]:4: error: expected pattern [ExpectedPattern]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_paren_match_regression.carbon:[[@LINE+2]]:12: error: expected `,` or `)` [UnexpectedTokenAfterListElement]
|
||||
// CHECK:STDERR: fail_paren_match_regression.carbon:[[@LINE+4]]:11: error: expected `,` or `)` [UnexpectedTokenAfterListElement]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_paren_match_regression.carbon:[[@LINE+2]]:15: error: `var` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR:
|
||||
var = (foo {})
|
||||
|
||||
// CHECK:STDERR: fail_paren_match_regression.carbon:[[@LINE+13]]:26: error: `var` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDOUT: - filename: fail_paren_match_regression.carbon
|
||||
// CHECK:STDOUT: ╭─FileStart ''
|
||||
// CHECK:STDOUT: │ ╭─VariableIntroducer 'var'
|
||||
|
||||
+10
-8
@@ -29,25 +29,27 @@ fn F() {
|
||||
// CHECK:STDERR: fail_invalid_form_literal.carbon:[[@LINE+2]]:8: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
form();
|
||||
// CHECK:STDERR: fail_invalid_form_literal.carbon:[[@LINE+2]]:8-10: error: expected `ref`, `var`, or `val` after `form(` [ExpectedCategoryModifier]
|
||||
// CHECK:STDERR: fail_invalid_form_literal.carbon:[[@LINE+2]]:8: error: expected `ref`, `var`, or `val` after `form(` [ExpectedCategoryModifier]
|
||||
// CHECK:STDERR:
|
||||
form(i32);
|
||||
// CHECK:STDERR: fail_invalid_form_literal.carbon:[[@LINE+4]]:8-12: error: expected `ref`, `var`, or `val` after `form(` [ExpectedCategoryModifier]
|
||||
// CHECK:STDERR: fail_invalid_form_literal.carbon:[[@LINE+5]]:8: error: expected `ref`, `var`, or `val` after `form(` [ExpectedCategoryModifier]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_invalid_form_literal.carbon:[[@LINE+2]]:14-16: error: unexpected tokens before `)` [ExpectedCloseSymbol]
|
||||
// CHECK:STDERR: fail_invalid_form_literal.carbon:[[@LINE+3]]:14-28: error: unexpected tokens before `)` [ExpectedCloseSymbol]
|
||||
// CHECK:STDERR: fail_invalid_form_literal.carbon:[[@LINE+2]]:7: note: matching this `(` [GroupOpenedHere]
|
||||
// CHECK:STDERR:
|
||||
form(whole lot of nonsense);
|
||||
// CHECK:STDERR: fail_invalid_form_literal.carbon:[[@LINE+2]]:18-20: error: unexpected tokens before `)` [ExpectedCloseSymbol]
|
||||
// CHECK:STDERR: fail_invalid_form_literal.carbon:[[@LINE+3]]:18-32: error: unexpected tokens before `)` [ExpectedCloseSymbol]
|
||||
// CHECK:STDERR: fail_invalid_form_literal.carbon:[[@LINE+2]]:7: note: matching this `(` [GroupOpenedHere]
|
||||
// CHECK:STDERR:
|
||||
form(var whole lot of nonsense);
|
||||
// CHECK:STDERR: fail_invalid_form_literal.carbon:[[@LINE+4]]:8-12: error: expected `(` after `form` [ExpectedParenAfter]
|
||||
// CHECK:STDERR: fail_invalid_form_literal.carbon:[[@LINE+4]]:8: error: expected `(` after `form` [ExpectedParenAfter]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_invalid_form_literal.carbon:[[@LINE+2]]:8-12: error: expected `;` after expression statement [ExpectedExprSemi]
|
||||
// CHECK:STDERR: fail_invalid_form_literal.carbon:[[@LINE+2]]:7: error: expected `;` after expression statement [ExpectedExprSemi]
|
||||
// CHECK:STDERR:
|
||||
form whole lot of nonsense;
|
||||
// CHECK:STDERR: fail_invalid_form_literal.carbon:[[@LINE+4]]:8-10: error: expected `(` after `form` [ExpectedParenAfter]
|
||||
// CHECK:STDERR: fail_invalid_form_literal.carbon:[[@LINE+4]]:8: error: expected `(` after `form` [ExpectedParenAfter]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_invalid_form_literal.carbon:[[@LINE+2]]:8-10: error: expected `;` after expression statement [ExpectedExprSemi]
|
||||
// CHECK:STDERR: fail_invalid_form_literal.carbon:[[@LINE+2]]:7: error: expected `;` after expression statement [ExpectedExprSemi]
|
||||
// CHECK:STDERR:
|
||||
form var whole lot of nonsense;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ fn F() {
|
||||
// The error skips past the string literal's group and the tokens after the
|
||||
// closing delimiter, ending the declaration at the `;` so that the following
|
||||
// declaration parses cleanly.
|
||||
// CHECK:STDERR: fail_string_in_skipped_group.carbon:[[@LINE+2]]:20-22: error: `var` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR: fail_string_in_skipped_group.carbon:[[@LINE+2]]:19: error: `var` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR:
|
||||
var x: i32 = bad bad ('''
|
||||
str
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/choice/fail_keyword_alternative_name.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/choice/fail_keyword_alternative_name.carbon
|
||||
|
||||
choice C {
|
||||
// CHECK:STDERR: fail_keyword_alternative_name.carbon:[[@LINE+2]]:3-7: error: expected choice alternative name; prefix reserved word with `r#` to form a valid identifier [ExpectedChoiceAlternativeName]
|
||||
// CHECK:STDERR:
|
||||
class,
|
||||
Other
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: - filename: fail_keyword_alternative_name.carbon
|
||||
// CHECK:STDOUT: ╭─FileStart ''
|
||||
// CHECK:STDOUT: │ ╭─ChoiceIntroducer 'choice'
|
||||
// CHECK:STDOUT: │ ├─IdentifierNameNotBeforeSignature 'C'
|
||||
// CHECK:STDOUT: │ ╭─ChoiceDefinitionStart '{'
|
||||
// CHECK:STDOUT: │ ├─IdentifierNameNotBeforeSignature 'class'
|
||||
// CHECK:STDOUT: │ ├─ChoiceAlternativeListComma ','
|
||||
// CHECK:STDOUT: │ ├─IdentifierNameNotBeforeSignature 'Other'
|
||||
// CHECK:STDOUT: ├─ChoiceDefinition '}'
|
||||
// CHECK:STDOUT: ├─FileEnd ''
|
||||
// CHECK:STDOUT: (root)
|
||||
+4
-4
@@ -36,7 +36,7 @@ adapt i32;
|
||||
// --- fail_bad_syntax.carbon
|
||||
|
||||
class A {
|
||||
// CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+2]]:11-24: error: `adapt` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+2]]:10: error: `adapt` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR:
|
||||
adapt 1 trailing_token;
|
||||
}
|
||||
@@ -48,13 +48,13 @@ class B {
|
||||
}
|
||||
|
||||
class C {
|
||||
// CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+2]]:8: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
adapt
|
||||
// CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+2]]:1: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
}
|
||||
|
||||
class D {
|
||||
// CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+2]]:9-13: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+2]]:8: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
adapt class;
|
||||
}
|
||||
|
||||
+5
-5
@@ -13,25 +13,25 @@ class A {
|
||||
// CHECK:STDERR:
|
||||
base;
|
||||
|
||||
// CHECK:STDERR: fail_base.carbon:[[@LINE+2]]:8-12: error: `class` or `:` expected after `base` [ExpectedAfterBase]
|
||||
// CHECK:STDERR: fail_base.carbon:[[@LINE+2]]:7: error: `class` or `:` expected after `base` [ExpectedAfterBase]
|
||||
// CHECK:STDERR:
|
||||
base calss X {}
|
||||
|
||||
// CHECK:STDERR: fail_base.carbon:[[@LINE+2]]:9: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR: fail_base.carbon:[[@LINE+2]]:8: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
base: ;
|
||||
|
||||
// CHECK:STDERR: fail_base.carbon:[[@LINE+2]]:12: error: `base` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR:
|
||||
base: { }
|
||||
// We should resume parsing here after the previous error.
|
||||
// CHECK:STDERR: fail_base.carbon:[[@LINE+2]]:3-5: error: `base` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR:
|
||||
var n: i32;
|
||||
}
|
||||
|
||||
base class Foo {}
|
||||
|
||||
class B {
|
||||
// CHECK:STDERR: fail_base.carbon:[[@LINE+2]]:15-17: error: `class` or `:` expected after `base` [ExpectedAfterBase]
|
||||
// CHECK:STDERR: fail_base.carbon:[[@LINE+2]]:14: error: `class` or `:` expected after `base` [ExpectedAfterBase]
|
||||
// CHECK:STDERR:
|
||||
extend base Foo;
|
||||
}
|
||||
|
||||
+4
-4
@@ -10,20 +10,20 @@
|
||||
|
||||
// --- fail_virtual_class.carbon
|
||||
|
||||
virtual class B
|
||||
// CHECK:STDERR: fail_virtual_class.carbon:[[@LINE+2]]:1: error: `virtual` declarations must either end with a `;` or have a `{ ... }` block for a definition [ExpectedDeclSemiOrDefinition]
|
||||
// CHECK:STDERR: fail_virtual_class.carbon:[[@LINE+2]]:16: error: `virtual` declarations must either end with a `;` or have a `{ ... }` block for a definition [ExpectedDeclSemiOrDefinition]
|
||||
// CHECK:STDERR:
|
||||
virtual class B
|
||||
|
||||
// --- fail_impl_class.carbon
|
||||
|
||||
// CHECK:STDERR: fail_impl_class.carbon:[[@LINE+2]]:1-4: error: `impl` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR: fail_impl_class.carbon:[[@LINE+2]]:11: error: `impl` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR:
|
||||
impl class
|
||||
|
||||
// --- fail_extend_adapt_base.carbon
|
||||
|
||||
class C {
|
||||
// CHECK:STDERR: fail_extend_adapt_base.carbon:[[@LINE+2]]:16-19: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR: fail_extend_adapt_base.carbon:[[@LINE+2]]:15: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
extend adapt base B;
|
||||
}
|
||||
|
||||
+5
-5
@@ -15,11 +15,11 @@ class C {
|
||||
|
||||
var name;
|
||||
|
||||
// CHECK:STDERR: fail_var_name.carbon:[[@LINE+2]]:7: error: expected pattern [ExpectedPattern]
|
||||
// CHECK:STDERR: fail_var_name.carbon:[[@LINE+2]]:6: error: expected pattern [ExpectedPattern]
|
||||
// CHECK:STDERR:
|
||||
var :;
|
||||
|
||||
// CHECK:STDERR: fail_var_name.carbon:[[@LINE+2]]:7: error: expected pattern [ExpectedPattern]
|
||||
// CHECK:STDERR: fail_var_name.carbon:[[@LINE+2]]:6: error: expected pattern [ExpectedPattern]
|
||||
// CHECK:STDERR:
|
||||
var : C;
|
||||
|
||||
@@ -27,17 +27,17 @@ class C {
|
||||
// CHECK:STDERR:
|
||||
var name:;
|
||||
|
||||
// CHECK:STDERR: fail_var_name.carbon:[[@LINE+2]]:12: error: `var` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR: fail_var_name.carbon:[[@LINE+2]]:11: error: `var` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR:
|
||||
var name C;
|
||||
|
||||
// CHECK:STDERR: fail_var_name.carbon:[[@LINE+2]]:15: error: `var` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR: fail_var_name.carbon:[[@LINE+2]]:14: error: `var` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR:
|
||||
var name: C C;
|
||||
|
||||
var self: C;
|
||||
|
||||
// CHECK:STDERR: fail_var_name.carbon:[[@LINE+2]]:12-15: error: `var` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR: fail_var_name.carbon:[[@LINE+2]]:11: error: `var` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR:
|
||||
var name name: C;
|
||||
|
||||
|
||||
+8
-7
@@ -31,25 +31,25 @@ class A {
|
||||
}
|
||||
|
||||
class B {
|
||||
// CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+2]]:12-22: error: `friend` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+2]]:11: error: `friend` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR:
|
||||
friend X extra_token;
|
||||
}
|
||||
|
||||
class C {
|
||||
// CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+2]]:9: error: expected identifier for friend declaration [ExpectedIdentifierForFriend]
|
||||
// CHECK:STDERR:
|
||||
friend
|
||||
// CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+2]]:1: error: expected identifier for friend declaration [ExpectedIdentifierForFriend]
|
||||
// CHECK:STDERR:
|
||||
}
|
||||
|
||||
class D {
|
||||
// CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+2]]:11: error: `friend` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR:
|
||||
friend X
|
||||
// CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+2]]:1: error: `friend` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR:
|
||||
}
|
||||
|
||||
class E {
|
||||
// CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+2]]:10-14: error: expected identifier for friend declaration [ExpectedIdentifierForFriend]
|
||||
// CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+2]]:10-14: error: expected identifier for friend declaration; prefix reserved word with `r#` to form a valid identifier [ExpectedIdentifierForFriend]
|
||||
// CHECK:STDERR:
|
||||
friend class;
|
||||
}
|
||||
@@ -108,7 +108,8 @@ class E {
|
||||
// CHECK:STDOUT: │ ├─IdentifierNameNotBeforeSignature 'E'
|
||||
// CHECK:STDOUT: │ ╭─ClassDefinitionStart '{'
|
||||
// CHECK:STDOUT: │ │ ╭─FriendIntroducer 'friend'
|
||||
// CHECK:STDOUT: │ ├─FriendDecl ';' has_error
|
||||
// CHECK:STDOUT: │ │ ├─IdentifierNameExpr 'class'
|
||||
// CHECK:STDOUT: │ ├─FriendDecl ';'
|
||||
// CHECK:STDOUT: ├─ClassDefinition '}'
|
||||
// CHECK:STDOUT: ├─FileEnd ''
|
||||
// CHECK:STDOUT: (root)
|
||||
+6
-6
@@ -12,13 +12,13 @@ fn F() {
|
||||
// CHECK:STDERR: fail_missing_cond.carbon:[[@LINE+2]]:7: error: expected `(` after `for` [ExpectedParenAfter]
|
||||
// CHECK:STDERR:
|
||||
for {
|
||||
// CHECK:STDERR: fail_missing_cond.carbon:[[@LINE+6]]:4: error: expected `in` after loop pattern [ExpectedIn]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_missing_cond.carbon:[[@LINE+4]]:4: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_missing_cond.carbon:[[@LINE+2]]:4: error: expected braced code block [ExpectedCodeBlock]
|
||||
// CHECK:STDERR:
|
||||
}
|
||||
// CHECK:STDERR: fail_missing_cond.carbon:[[@LINE+8]]:1: error: expected `in` after loop pattern [ExpectedIn]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_missing_cond.carbon:[[@LINE+6]]:1: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_missing_cond.carbon:[[@LINE+4]]:1: error: expected braced code block [ExpectedCodeBlock]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_missing_cond.carbon:[[@LINE+2]]:1: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
}
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/for/fail_missing_in.carbon
|
||||
|
||||
fn foo() {
|
||||
// CHECK:STDERR: fail_missing_in.carbon:[[@LINE+2]]:19: error: expected `in` after loop pattern [ExpectedIn]
|
||||
// CHECK:STDERR: fail_missing_in.carbon:[[@LINE+2]]:18: error: expected `in` after loop pattern [ExpectedIn]
|
||||
// CHECK:STDERR:
|
||||
for (var x: i32 y) {
|
||||
Print(x);
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/for/fail_returned_var.carbon
|
||||
|
||||
fn foo() -> i32 {
|
||||
// CHECK:STDERR: fail_returned_var.carbon:[[@LINE+2]]:8-15: error: expected pattern [ExpectedPattern]
|
||||
// CHECK:STDERR: fail_returned_var.carbon:[[@LINE+2]]:8: error: expected pattern [ExpectedPattern]
|
||||
// CHECK:STDERR:
|
||||
for (returned var x: i32 in y) {
|
||||
return var;
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
fn F() {
|
||||
// CHECK:STDERR: fail_square_brackets.carbon:[[@LINE+10]]:7: error: expected `(` after `for` [ExpectedParenAfter]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_square_brackets.carbon:[[@LINE+8]]:7: error: expected pattern [ExpectedPattern]
|
||||
// CHECK:STDERR: fail_square_brackets.carbon:[[@LINE+8]]:6: error: expected pattern [ExpectedPattern]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_square_brackets.carbon:[[@LINE+6]]:7: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR: fail_square_brackets.carbon:[[@LINE+6]]:6: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_square_brackets.carbon:[[@LINE+4]]:7: error: expected braced code block [ExpectedCodeBlock]
|
||||
// CHECK:STDERR:
|
||||
|
||||
+12
-12
@@ -45,7 +45,7 @@ private override default fn I();
|
||||
|
||||
// --- fail_identifier_instead_of_sig.carbon
|
||||
|
||||
// CHECK:STDERR: fail_identifier_instead_of_sig.carbon:[[@LINE+2]]:8-10: error: `fn` declarations must either end with a `;` or have a `{ ... }` block for a definition [ExpectedDeclSemiOrDefinition]
|
||||
// CHECK:STDERR: fail_identifier_instead_of_sig.carbon:[[@LINE+2]]:7: error: `fn` declarations must either end with a `;` or have a `{ ... }` block for a definition [ExpectedDeclSemiOrDefinition]
|
||||
// CHECK:STDERR:
|
||||
fn foo bar;
|
||||
|
||||
@@ -58,15 +58,15 @@ fn Div[();
|
||||
|
||||
// --- fail_missing_name.carbon
|
||||
|
||||
// CHECK:STDERR: fail_missing_name.carbon:[[@LINE+2]]:4: error: `fn` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR: fail_missing_name.carbon:[[@LINE+2]]:3: error: `fn` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR:
|
||||
fn ();
|
||||
|
||||
// --- fail_no_sig_or_semi.carbon
|
||||
|
||||
fn foo
|
||||
// CHECK:STDERR: fail_no_sig_or_semi.carbon:[[@LINE+2]]:1: error: `fn` declarations must either end with a `;` or have a `{ ... }` block for a definition [ExpectedDeclSemiOrDefinition]
|
||||
// CHECK:STDERR: fail_no_sig_or_semi.carbon:[[@LINE+2]]:7: error: `fn` declarations must either end with a `;` or have a `{ ... }` block for a definition [ExpectedDeclSemiOrDefinition]
|
||||
// CHECK:STDERR:
|
||||
fn foo
|
||||
|
||||
// --- fail_only_fn_and_semi.carbon
|
||||
|
||||
@@ -76,13 +76,13 @@ fn;
|
||||
|
||||
// --- fail_repeated_fn_and_semi.carbon
|
||||
|
||||
// CHECK:STDERR: fail_repeated_fn_and_semi.carbon:[[@LINE+2]]:4-5: error: `fn` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR: fail_repeated_fn_and_semi.carbon:[[@LINE+2]]:4-5: error: `fn` introducer should be followed by a name; prefix reserved word with `r#` to form a valid identifier [ExpectedDeclName]
|
||||
// CHECK:STDERR:
|
||||
fn fn;
|
||||
|
||||
// --- fail_skip_indented_newline_until_outdent.carbon
|
||||
|
||||
// CHECK:STDERR: fail_skip_indented_newline_until_outdent.carbon:[[@LINE+2]]:6: error: `fn` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR: fail_skip_indented_newline_until_outdent.carbon:[[@LINE+2]]:5: error: `fn` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR:
|
||||
fn (x,
|
||||
y,
|
||||
@@ -91,7 +91,7 @@ fn F();
|
||||
|
||||
// --- fail_skip_indented_newline_with_semi.carbon
|
||||
|
||||
// CHECK:STDERR: fail_skip_indented_newline_with_semi.carbon:[[@LINE+2]]:4: error: `fn` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR: fail_skip_indented_newline_with_semi.carbon:[[@LINE+2]]:3: error: `fn` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR:
|
||||
fn (x,
|
||||
y,
|
||||
@@ -100,7 +100,7 @@ fn F();
|
||||
|
||||
// --- fail_skip_indented_newline_without_semi.carbon
|
||||
|
||||
// CHECK:STDERR: fail_skip_indented_newline_without_semi.carbon:[[@LINE+2]]:4: error: `fn` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR: fail_skip_indented_newline_without_semi.carbon:[[@LINE+2]]:3: error: `fn` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR:
|
||||
fn (x,
|
||||
y,
|
||||
@@ -109,7 +109,7 @@ fn F();
|
||||
|
||||
// --- fail_skip_to_newline_without_semi.carbon
|
||||
|
||||
// CHECK:STDERR: fail_skip_to_newline_without_semi.carbon:[[@LINE+2]]:4: error: `fn` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR: fail_skip_to_newline_without_semi.carbon:[[@LINE+2]]:3: error: `fn` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR:
|
||||
fn ()
|
||||
fn F();
|
||||
@@ -127,7 +127,7 @@ fn foo(bar);
|
||||
|
||||
// --- fail_without_name_and_many_tokens_in_params.carbon
|
||||
|
||||
// CHECK:STDERR: fail_without_name_and_many_tokens_in_params.carbon:[[@LINE+2]]:4: error: `fn` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR: fail_without_name_and_many_tokens_in_params.carbon:[[@LINE+2]]:3: error: `fn` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR:
|
||||
fn (a tokens c d e f g h i j k l m n o p q r s t u v w x y z);
|
||||
|
||||
@@ -344,8 +344,8 @@ fn ValReturn() -> val;
|
||||
// CHECK:STDOUT: - filename: fail_repeated_fn_and_semi.carbon
|
||||
// CHECK:STDOUT: ╭─FileStart ''
|
||||
// CHECK:STDOUT: │ ╭─FunctionIntroducer 'fn'
|
||||
// CHECK:STDOUT: │ ├─InvalidParse 'fn' has_error
|
||||
// CHECK:STDOUT: ├─FunctionDecl ';' has_error
|
||||
// CHECK:STDOUT: │ ├─IdentifierNameNotBeforeSignature 'fn'
|
||||
// CHECK:STDOUT: ├─FunctionDecl ';'
|
||||
// CHECK:STDOUT: ├─FileEnd ''
|
||||
// CHECK:STDOUT: (root)
|
||||
// CHECK:STDOUT: - filename: fail_skip_indented_newline_until_outdent.carbon
|
||||
|
||||
+5
-5
@@ -35,9 +35,9 @@ fn foo() -> f64 {
|
||||
fn F() {
|
||||
// Note: this might become valid depending on the expression syntax. This test
|
||||
// shouldn't be taken as a sign it should remain invalid.
|
||||
// CHECK:STDERR: fail_identifier_in_statements.carbon:[[@LINE+2]]:6: error: expected `;` after expression statement [ExpectedExprSemi]
|
||||
// CHECK:STDERR:
|
||||
bar
|
||||
// CHECK:STDERR: fail_identifier_in_statements.carbon:[[@LINE+2]]:1: error: expected `;` after expression statement [ExpectedExprSemi]
|
||||
// CHECK:STDERR:
|
||||
}
|
||||
|
||||
// --- nested.carbon
|
||||
@@ -59,19 +59,19 @@ impl T as I {
|
||||
|
||||
// --- fail_builtin_identifier.carbon
|
||||
|
||||
// CHECK:STDERR: fail_builtin_identifier.carbon:[[@LINE+2]]:18-23: error: expected builtin function name after `=` [ExpectedBuiltinName]
|
||||
// CHECK:STDERR: fail_builtin_identifier.carbon:[[@LINE+2]]:17: error: expected builtin function name after `=` [ExpectedBuiltinName]
|
||||
// CHECK:STDERR:
|
||||
fn NotString() = banana;
|
||||
|
||||
// --- fail_builtin_two_strings.carbon
|
||||
|
||||
// CHECK:STDERR: fail_builtin_two_strings.carbon:[[@LINE+2]]:32-38: error: `fn` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR: fail_builtin_two_strings.carbon:[[@LINE+2]]:31: error: `fn` declarations must end with a `;` [ExpectedDeclSemi]
|
||||
// CHECK:STDERR:
|
||||
fn JunkAfterString() = "hello" "world";
|
||||
|
||||
// --- fail_builtin_spurious_equals.carbon
|
||||
|
||||
// CHECK:STDERR: fail_builtin_spurious_equals.carbon:[[@LINE+2]]:23: error: expected builtin function name after `=` [ExpectedBuiltinName]
|
||||
// CHECK:STDERR: fail_builtin_spurious_equals.carbon:[[@LINE+2]]:22: error: expected builtin function name after `=` [ExpectedBuiltinName]
|
||||
// CHECK:STDERR:
|
||||
fn SpuriousEquals() = { }
|
||||
fn TestRecoveryFromSpuriousEquals();
|
||||
|
||||
+2
-2
@@ -22,13 +22,13 @@ extern library default fn G();
|
||||
|
||||
// --- fail_name_missing.carbon
|
||||
|
||||
// CHECK:STDERR: fail_name_missing.carbon:[[@LINE+2]]:16-17: error: expected `default` or a string literal to specify the library name [ExpectedLibraryNameOrDefault]
|
||||
// CHECK:STDERR: fail_name_missing.carbon:[[@LINE+2]]:15: error: expected `default` or a string literal to specify the library name [ExpectedLibraryNameOrDefault]
|
||||
// CHECK:STDERR:
|
||||
extern library fn G();
|
||||
|
||||
// --- fail_identifier.carbon
|
||||
|
||||
// CHECK:STDERR: fail_identifier.carbon:[[@LINE+4]]:16-18: error: expected `default` or a string literal to specify the library name [ExpectedLibraryNameOrDefault]
|
||||
// CHECK:STDERR: fail_identifier.carbon:[[@LINE+4]]:15: error: expected `default` or a string literal to specify the library name [ExpectedLibraryNameOrDefault]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_identifier.carbon:[[@LINE+2]]:16-18: error: unrecognized declaration introducer [UnrecognizedDecl]
|
||||
// CHECK:STDERR:
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@
|
||||
|
||||
// --- fail_template_before_type.carbon
|
||||
|
||||
// CHECK:STDERR: fail_template_before_type.carbon:[[@LINE+2]]:15-17: error: expected name in binding pattern [ExpectedBindingPattern]
|
||||
// CHECK:STDERR: fail_template_before_type.carbon:[[@LINE+2]]:14: error: expected name in binding pattern [ExpectedBindingPattern]
|
||||
// CHECK:STDERR:
|
||||
fn A(template i32);
|
||||
|
||||
@@ -22,7 +22,7 @@ fn C(template);
|
||||
|
||||
// --- fail_reserved_word_after_generic.carbon
|
||||
|
||||
// CHECK:STDERR: fail_reserved_word_after_generic.carbon:[[@LINE+2]]:22: error: expected name in binding pattern; prefix reserved word with `r#` to form a valid identifier [ExpectedBindingPattern]
|
||||
// CHECK:STDERR: fail_reserved_word_after_generic.carbon:[[@LINE+2]]:14-21: error: expected name in binding pattern; prefix reserved word with `r#` to form a valid identifier [ExpectedBindingPattern]
|
||||
// CHECK:STDERR:
|
||||
fn E(generic template: i32);
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ fn foo(template ref a: i32);
|
||||
// CHECK:STDERR:
|
||||
fn foo(template ref a: i32);
|
||||
|
||||
// CHECK:STDERR: fail_template_ref.carbon:[[@LINE+2]]:12-19: error: expected name in binding pattern [ExpectedBindingPattern]
|
||||
// CHECK:STDERR: fail_template_ref.carbon:[[@LINE+2]]:11: error: expected name in binding pattern [ExpectedBindingPattern]
|
||||
// CHECK:STDERR:
|
||||
fn foo(ref template a: i32);
|
||||
|
||||
|
||||
+10
-10
@@ -8,7 +8,7 @@
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/generics/impl/fail_impl.carbon
|
||||
|
||||
// CHECK:STDERR: fail_impl.carbon:[[@LINE+2]]:10-12: error: expected `as` in `impl` declaration [ImplExpectedAs]
|
||||
// CHECK:STDERR: fail_impl.carbon:[[@LINE+2]]:9: error: expected `as` in `impl` declaration [ImplExpectedAs]
|
||||
// CHECK:STDERR:
|
||||
impl foo bar;
|
||||
|
||||
@@ -16,19 +16,19 @@ impl foo bar;
|
||||
// CHECK:STDERR:
|
||||
impl i32 as;
|
||||
|
||||
// CHECK:STDERR: fail_impl.carbon:[[@LINE+2]]:18-27: error: `impl` declarations must either end with a `;` or have a `{ ... }` block for a definition [ExpectedDeclSemiOrDefinition]
|
||||
// CHECK:STDERR: fail_impl.carbon:[[@LINE+2]]:17: error: `impl` declarations must either end with a `;` or have a `{ ... }` block for a definition [ExpectedDeclSemiOrDefinition]
|
||||
// CHECK:STDERR:
|
||||
impl bool as bar unexpected;
|
||||
|
||||
// CHECK:STDERR: fail_impl.carbon:[[@LINE+2]]:6-11: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR: fail_impl.carbon:[[@LINE+2]]:5: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
impl return as A;
|
||||
|
||||
// CHECK:STDERR: fail_impl.carbon:[[@LINE+2]]:6-11: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR: fail_impl.carbon:[[@LINE+2]]:5: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
impl return B;
|
||||
|
||||
// CHECK:STDERR: fail_impl.carbon:[[@LINE+4]]:13-15: error: expected `[` after `forall` in `impl` declaration [ImplExpectedAfterForall]
|
||||
// CHECK:STDERR: fail_impl.carbon:[[@LINE+4]]:13: error: expected `[` after `forall` in `impl` declaration [ImplExpectedAfterForall]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_impl.carbon:[[@LINE+2]]:16: error: expected `as` in `impl` declaration [ImplExpectedAs]
|
||||
// CHECK:STDERR:
|
||||
@@ -42,7 +42,7 @@ impl forall [] u32;
|
||||
// CHECK:STDERR:
|
||||
impl forall [invalid] i8;
|
||||
|
||||
// CHECK:STDERR: fail_impl.carbon:[[@LINE+2]]:13-15: error: expected `[` after `forall` in `impl` declaration [ImplExpectedAfterForall]
|
||||
// CHECK:STDERR: fail_impl.carbon:[[@LINE+2]]:13: error: expected `[` after `forall` in `impl` declaration [ImplExpectedAfterForall]
|
||||
// CHECK:STDERR:
|
||||
impl forall f16 as Quux;
|
||||
|
||||
@@ -50,11 +50,11 @@ impl forall f16 as Quux;
|
||||
// CHECK:STDERR:
|
||||
impl forall [T: type] str;
|
||||
|
||||
// CHECK:STDERR: fail_impl.carbon:[[@LINE+2]]:25-34: error: expected `as` in `impl` declaration [ImplExpectedAs]
|
||||
// CHECK:STDERR: fail_impl.carbon:[[@LINE+2]]:24: error: expected `as` in `impl` declaration [ImplExpectedAs]
|
||||
// CHECK:STDERR:
|
||||
impl forall [T: type] T missing_as;
|
||||
|
||||
// CHECK:STDERR: fail_impl.carbon:[[@LINE+2]]:38-42: error: `impl` declarations must either end with a `;` or have a `{ ... }` block for a definition [ExpectedDeclSemiOrDefinition]
|
||||
// CHECK:STDERR: fail_impl.carbon:[[@LINE+2]]:37: error: `impl` declarations must either end with a `;` or have a `{ ... }` block for a definition [ExpectedDeclSemiOrDefinition]
|
||||
// CHECK:STDERR:
|
||||
impl forall [T: type] T as Interface extra;
|
||||
|
||||
@@ -62,10 +62,10 @@ impl forall [T: type] T as Interface extra;
|
||||
// CHECK:STDERR:
|
||||
impl;
|
||||
|
||||
// CHECK:STDERR: fail_impl.carbon:[[@LINE+2]]:5: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
impl
|
||||
|
||||
// CHECK:STDERR: fail_impl.carbon:[[@LINE+87]]:26: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDOUT: - filename: fail_impl.carbon
|
||||
// CHECK:STDOUT: ╭─FileStart ''
|
||||
// CHECK:STDOUT: │ ╭─ImplIntroducer 'impl'
|
||||
|
||||
@@ -18,7 +18,7 @@ impl bool as Interface {
|
||||
fn F();
|
||||
}
|
||||
|
||||
// CHECK:STDERR: fail_out_of_line_member.carbon:[[@LINE+2]]:4: error: `fn` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR: fail_out_of_line_member.carbon:[[@LINE+2]]:3: error: `fn` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR:
|
||||
fn (bool as Interface).F() {}
|
||||
|
||||
|
||||
@@ -20,9 +20,9 @@ match_first;
|
||||
|
||||
// --- fail_eof.carbon
|
||||
|
||||
match_first
|
||||
// CHECK:STDERR: fail_eof.carbon:[[@LINE+2]]:1: error: expected `{` after `match_first` [ExpectedCurlyBraceAfter]
|
||||
// CHECK:STDERR: fail_eof.carbon:[[@LINE+2]]:12: error: expected `{` after `match_first` [ExpectedCurlyBraceAfter]
|
||||
// CHECK:STDERR:
|
||||
match_first
|
||||
|
||||
// --- with_impls.carbon
|
||||
|
||||
|
||||
+10
-6
@@ -13,7 +13,7 @@
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
interface I {
|
||||
// CHECK:STDERR: fail_tuple_pattern.carbon:[[@LINE+2]]:7: error: expected identifier in associated constant declaration [ExpectedAssociatedConstantIdentifier]
|
||||
// CHECK:STDERR: fail_tuple_pattern.carbon:[[@LINE+2]]:6: error: expected identifier in associated constant declaration [ExpectedAssociatedConstantIdentifier]
|
||||
// CHECK:STDERR:
|
||||
let (T: type, U: type);
|
||||
}
|
||||
@@ -23,7 +23,7 @@ interface I {
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
interface I {
|
||||
// CHECK:STDERR: fail_tuple_pattern_with_default.carbon:[[@LINE+2]]:15: error: expected identifier in associated constant declaration [ExpectedAssociatedConstantIdentifier]
|
||||
// CHECK:STDERR: fail_tuple_pattern_with_default.carbon:[[@LINE+2]]:14: error: expected identifier in associated constant declaration [ExpectedAssociatedConstantIdentifier]
|
||||
// CHECK:STDERR:
|
||||
default let (T: type, U: type) = ({}, {});
|
||||
}
|
||||
@@ -33,7 +33,7 @@ interface I {
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
interface I {
|
||||
// CHECK:STDERR: fail_var_pattern.carbon:[[@LINE+2]]:7-9: error: expected identifier in associated constant declaration [ExpectedAssociatedConstantIdentifier]
|
||||
// CHECK:STDERR: fail_var_pattern.carbon:[[@LINE+2]]:6: error: expected identifier in associated constant declaration [ExpectedAssociatedConstantIdentifier]
|
||||
// CHECK:STDERR:
|
||||
let var T: type;
|
||||
}
|
||||
@@ -43,7 +43,7 @@ interface I {
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
interface I {
|
||||
// CHECK:STDERR: fail_var_pattern_with_default.carbon:[[@LINE+2]]:15-17: error: expected identifier in associated constant declaration [ExpectedAssociatedConstantIdentifier]
|
||||
// CHECK:STDERR: fail_var_pattern_with_default.carbon:[[@LINE+2]]:14: error: expected identifier in associated constant declaration [ExpectedAssociatedConstantIdentifier]
|
||||
// CHECK:STDERR:
|
||||
default let var T: type = {};
|
||||
}
|
||||
@@ -83,7 +83,7 @@ interface I {
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
interface I {
|
||||
// CHECK:STDERR: fail_underscore.carbon:[[@LINE+2]]:7: error: expected identifier in associated constant declaration [ExpectedAssociatedConstantIdentifier]
|
||||
// CHECK:STDERR: fail_underscore.carbon:[[@LINE+2]]:7: error: expected identifier in associated constant declaration; prefix reserved word with `r#` to form a valid identifier [ExpectedAssociatedConstantIdentifier]
|
||||
// CHECK:STDERR:
|
||||
let _: {};
|
||||
}
|
||||
@@ -201,7 +201,11 @@ interface I {
|
||||
// CHECK:STDOUT: │ ├─IdentifierNameNotBeforeSignature 'I'
|
||||
// CHECK:STDOUT: │ ╭─InterfaceDefinitionStart '{'
|
||||
// CHECK:STDOUT: │ │ ╭─AssociatedConstantIntroducer 'let'
|
||||
// CHECK:STDOUT: │ ├─AssociatedConstantDecl ';' has_error
|
||||
// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature '_'
|
||||
// CHECK:STDOUT: │ │ │ │ ╭─StructLiteralStart '{'
|
||||
// CHECK:STDOUT: │ │ │ ├─StructLiteral '}'
|
||||
// CHECK:STDOUT: │ │ ├─AssociatedConstantNameAndType ':'
|
||||
// CHECK:STDOUT: │ ├─AssociatedConstantDecl ';'
|
||||
// CHECK:STDOUT: ├─InterfaceDefinition '}'
|
||||
// CHECK:STDOUT: ├─FileEnd ''
|
||||
// CHECK:STDOUT: (root)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/generics/interface/fail_missing_name.carbon
|
||||
|
||||
// CHECK:STDERR: fail_missing_name.carbon:[[@LINE+2]]:11: error: `interface` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR: fail_missing_name.carbon:[[@LINE+2]]:10: error: `interface` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR:
|
||||
interface {
|
||||
}
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/generics/interface/fail_missing_open_curly.carbon
|
||||
|
||||
// CHECK:STDERR: fail_missing_open_curly.carbon:[[@LINE+2]]:15-17: error: `interface` declarations must either end with a `;` or have a `{ ... }` block for a definition [ExpectedDeclSemiOrDefinition]
|
||||
// CHECK:STDERR: fail_missing_open_curly.carbon:[[@LINE+2]]:14: error: `interface` declarations must either end with a `;` or have a `{ ... }` block for a definition [ExpectedDeclSemiOrDefinition]
|
||||
// CHECK:STDERR:
|
||||
interface Bar Baz {}
|
||||
|
||||
// CHECK:STDERR: fail_missing_open_curly.carbon:[[@LINE+2]]:14: error: `interface` declarations must either end with a `;` or have a `{ ... }` block for a definition [ExpectedDeclSemiOrDefinition]
|
||||
// CHECK:STDERR:
|
||||
interface Foo
|
||||
|
||||
// CHECK:STDERR: fail_missing_open_curly.carbon:[[@LINE+11]]:26: error: `interface` declarations must either end with a `;` or have a `{ ... }` block for a definition [ExpectedDeclSemiOrDefinition]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDOUT: - filename: fail_missing_open_curly.carbon
|
||||
// CHECK:STDOUT: ╭─FileStart ''
|
||||
// CHECK:STDOUT: │ ╭─InterfaceIntroducer 'interface'
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/generics/interface/fail_self_param_syntax.carbon
|
||||
|
||||
interface Foo {
|
||||
// CHECK:STDERR: fail_self_param_syntax.carbon:[[@LINE+2]]:13-16: error: expected `,` or `]` [UnexpectedTokenAfterListElement]
|
||||
// CHECK:STDERR: fail_self_param_syntax.carbon:[[@LINE+2]]:12: error: expected `,` or `]` [UnexpectedTokenAfterListElement]
|
||||
// CHECK:STDERR:
|
||||
fn Sub[me Self](b: Self) -> Self;
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ interface Z {
|
||||
interface Y {}
|
||||
|
||||
interface Z {
|
||||
// CHECK:STDERR: fail_wrong_keyword.carbon:[[@LINE+2]]:11-12: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR: fail_wrong_keyword.carbon:[[@LINE+2]]:10: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
require as Y;
|
||||
}
|
||||
@@ -81,7 +81,7 @@ interface Z {
|
||||
interface Y {}
|
||||
|
||||
interface Z {
|
||||
// CHECK:STDERR: fail_as_in_self.carbon:[[@LINE+2]]:16-17: error: expected `impls` in `require` declaration [RequireExpectedImpls]
|
||||
// CHECK:STDERR: fail_as_in_self.carbon:[[@LINE+2]]:15: error: expected `impls` in `require` declaration [RequireExpectedImpls]
|
||||
// CHECK:STDERR:
|
||||
require Self as Y impls Y;
|
||||
}
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/generics/named_constraint/fail_incomplete.carbon
|
||||
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE+2]]:12-16: error: `constraint` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE+2]]:11: error: `constraint` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR:
|
||||
constraint "Foo"
|
||||
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE+2]]:1-10: error: `constraint` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE+2]]:11: error: `constraint` introducer should be followed by a name [ExpectedDeclName]
|
||||
// CHECK:STDERR:
|
||||
constraint
|
||||
|
||||
|
||||
+9
-9
@@ -9,20 +9,20 @@
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/if/fail_else_unbraced.carbon
|
||||
|
||||
fn F() {
|
||||
// CHECK:STDERR: fail_else_unbraced.carbon:[[@LINE+2]]:9: error: expected braced code block [ExpectedCodeBlock]
|
||||
// CHECK:STDERR:
|
||||
if (a)
|
||||
// CHECK:STDERR: fail_else_unbraced.carbon:[[@LINE+2]]:5-6: error: expected braced code block [ExpectedCodeBlock]
|
||||
// CHECK:STDERR: fail_else_unbraced.carbon:[[@LINE+2]]:11: error: expected braced code block [ExpectedCodeBlock]
|
||||
// CHECK:STDERR:
|
||||
if (b)
|
||||
// CHECK:STDERR: fail_else_unbraced.carbon:[[@LINE+2]]:7: error: expected braced code block [ExpectedCodeBlock]
|
||||
// CHECK:STDERR:
|
||||
c;
|
||||
else
|
||||
// CHECK:STDERR: fail_else_unbraced.carbon:[[@LINE+2]]:7: error: expected braced code block [ExpectedCodeBlock]
|
||||
// CHECK:STDERR:
|
||||
d;
|
||||
else
|
||||
// CHECK:STDERR: fail_else_unbraced.carbon:[[@LINE+2]]:5: error: expected braced code block [ExpectedCodeBlock]
|
||||
// CHECK:STDERR: fail_else_unbraced.carbon:[[@LINE+2]]:9: error: expected braced code block [ExpectedCodeBlock]
|
||||
// CHECK:STDERR:
|
||||
else
|
||||
d;
|
||||
// CHECK:STDERR: fail_else_unbraced.carbon:[[@LINE+2]]:7: error: expected braced code block [ExpectedCodeBlock]
|
||||
// CHECK:STDERR:
|
||||
else
|
||||
e;
|
||||
if (x) { f; }
|
||||
else if (x) { g; }
|
||||
|
||||
+4
-3
@@ -15,12 +15,13 @@ fn F() {
|
||||
// CHECK:STDERR: fail_errors.carbon:[[@LINE+2]]:7: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
if () {}
|
||||
// CHECK:STDERR: fail_errors.carbon:[[@LINE+2]]:9: error: unexpected tokens before `)` [ExpectedCloseSymbol]
|
||||
// CHECK:STDERR: fail_errors.carbon:[[@LINE+3]]:9: error: unexpected tokens before `)` [ExpectedCloseSymbol]
|
||||
// CHECK:STDERR: fail_errors.carbon:[[@LINE+2]]:6: note: matching this `(` [GroupOpenedHere]
|
||||
// CHECK:STDERR:
|
||||
if (b c) {}
|
||||
// CHECK:STDERR: fail_errors.carbon:[[@LINE+2]]:9: error: expected braced code block [ExpectedCodeBlock]
|
||||
// CHECK:STDERR:
|
||||
if (d)
|
||||
// CHECK:STDERR: fail_errors.carbon:[[@LINE+4]]:1: error: expected braced code block [ExpectedCodeBlock]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_errors.carbon:[[@LINE+2]]:1: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
}
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
fn F() {
|
||||
// CHECK:STDERR: fail_square_brackets.carbon:[[@LINE+8]]:6: error: expected `(` after `if` [ExpectedParenAfter]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_square_brackets.carbon:[[@LINE+6]]:6: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR: fail_square_brackets.carbon:[[@LINE+6]]:5: error: expected expression [ExpectedExpr]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_square_brackets.carbon:[[@LINE+4]]:6: error: expected braced code block [ExpectedCodeBlock]
|
||||
// CHECK:STDERR:
|
||||
|
||||
+4
-4
@@ -10,15 +10,15 @@
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/if/fail_unbraced.carbon
|
||||
|
||||
fn F() {
|
||||
// CHECK:STDERR: fail_unbraced.carbon:[[@LINE+2]]:9: error: expected braced code block [ExpectedCodeBlock]
|
||||
// CHECK:STDERR:
|
||||
if (a)
|
||||
// CHECK:STDERR: fail_unbraced.carbon:[[@LINE+2]]:5-6: error: expected braced code block [ExpectedCodeBlock]
|
||||
// CHECK:STDERR: fail_unbraced.carbon:[[@LINE+2]]:11: error: expected braced code block [ExpectedCodeBlock]
|
||||
// CHECK:STDERR:
|
||||
if (b)
|
||||
// CHECK:STDERR: fail_unbraced.carbon:[[@LINE+2]]:7-8: error: expected braced code block [ExpectedCodeBlock]
|
||||
// CHECK:STDERR: fail_unbraced.carbon:[[@LINE+2]]:13: error: expected braced code block [ExpectedCodeBlock]
|
||||
// CHECK:STDERR:
|
||||
if (c)
|
||||
// CHECK:STDERR: fail_unbraced.carbon:[[@LINE+2]]:9: error: expected braced code block [ExpectedCodeBlock]
|
||||
// CHECK:STDERR:
|
||||
d;
|
||||
}
|
||||
|
||||
|
||||
Loaded 100 of 173 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user