Skip to content

Commit 7254987

Browse files
authored
Fix cryptic error when keyword is used as an object key (#8001)
1 parent 3abc63f commit 7254987

File tree

1 file changed

+33
-1
lines changed

1 file changed

+33
-1
lines changed

rust/kcl-lib/src/parsing/parser.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ const IF_ELSE_CANNOT_BE_EMPTY: &str = "`if` and `else` blocks cannot be empty";
4848
const ELSE_STRUCTURE: &str = "This `else` should be followed by a {, then a block of code, then a }";
4949
const ELSE_MUST_END_IN_EXPR: &str = "This `else` block needs to end in an expression, which will be the value if no preceding `if` condition was matched";
5050

51+
const KEYWORD_EXPECTING_IDENTIFIER: &str = "Expected an identifier, but found a reserved keyword.";
52+
5153
pub fn run_parser(i: TokenSlice) -> super::ParseResult {
5254
let _stats = crate::log::LogPerfStats::new("Parsing");
5355
ParseContext::init();
@@ -930,7 +932,7 @@ fn object_property_same_key_and_val(i: &mut TokenSlice) -> ModalResult<Node<Obje
930932
}
931933

932934
fn object_property(i: &mut TokenSlice) -> ModalResult<Node<ObjectProperty>> {
933-
let key = identifier.context(expected("the property's key (the name or identifier of the property), e.g. in 'height = 4', 'height' is the property key")).parse_next(i)?;
935+
let key_token = identifier_or_keyword.context(expected("the property's key (the name or identifier of the property), e.g. in 'height = 4', 'height' is the property key")).parse_next(i)?;
934936
ignore_whitespace(i);
935937
// Temporarily accept both `:` and `=` for compatibility.
936938
let sep = alt((colon, equals))
@@ -957,6 +959,18 @@ fn object_property(i: &mut TokenSlice) -> ModalResult<Node<ObjectProperty>> {
957959
}
958960
};
959961

962+
// Now that we've verified that we can parse everything, ensure that the key
963+
// is valid. If not, we can cut.
964+
let key = Node::<Identifier>::try_from(key_token).map_err(|comp_err| {
965+
ErrMode::Cut(ContextError {
966+
context: Default::default(),
967+
cause: Some(CompilationError::err(
968+
comp_err.source_range,
969+
KEYWORD_EXPECTING_IDENTIFIER,
970+
)),
971+
})
972+
})?;
973+
960974
let result = Node::new_node(
961975
key.start,
962976
expr.end(),
@@ -2427,6 +2441,12 @@ fn identifier(i: &mut TokenSlice) -> ModalResult<Node<Identifier>> {
24272441
.parse_next(i)
24282442
}
24292443

2444+
fn identifier_or_keyword(i: &mut TokenSlice) -> ModalResult<Token> {
2445+
any.verify(|token: &Token| token.token_type == TokenType::Word || token.token_type == TokenType::Keyword)
2446+
.context(expected("an identifier or keyword"))
2447+
.parse_next(i)
2448+
}
2449+
24302450
fn nameable_identifier(i: &mut TokenSlice) -> ModalResult<Node<Identifier>> {
24312451
let result = identifier.parse_next(i)?;
24322452

@@ -5396,6 +5416,18 @@ bar = 1
53965416
assert_eq!(cause.err.source_range.start(), expected_src_start);
53975417
}
53985418

5419+
#[test]
5420+
fn test_sensible_error_when_using_keyword_as_object_key() {
5421+
let source = r#"material = {
5422+
type = "Steel 45"
5423+
}"#;
5424+
let expected_src_start = source.find("type").unwrap();
5425+
let cause = must_fail_compilation(source);
5426+
assert!(cause.was_fatal);
5427+
assert_eq!(cause.err.message, KEYWORD_EXPECTING_IDENTIFIER);
5428+
assert_eq!(cause.err.source_range.start(), expected_src_start);
5429+
}
5430+
53995431
struct MustFail {
54005432
err: CompilationError,
54015433
was_fatal: bool,

0 commit comments

Comments
 (0)