diff --git a/releasenotes.md b/releasenotes.md index e3502eb37..8b9160650 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -10,6 +10,7 @@ - Allow the right hand side of `|||` and `&&&` be runtime values. - Added `@rnd()` compile time random function (using the `$$rnd()` builtin). #2078 - Add `math::@ceil()` compile time ceil function. #2134 +- Improve error message when using keywords as functions/macros/variables #2133. ### Fixes - Assert triggered when casting from `int[2]` to `uint[2]` #2115 diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 961214ed3..74f988976 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -1173,6 +1173,7 @@ typedef enum // Keywords TOKEN_ALIAS, + TOKEN_FIRST_NON_TYPE_KEYWORD = TOKEN_ALIAS, TOKEN_ASSERT, TOKEN_ASM, TOKEN_ATTRDEF, diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index d3638a511..9b5a0f2ae 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -1609,6 +1609,10 @@ bool parse_parameters(ParseContext *c, Decl ***params_ref, Variadic *variadic, i param_kind = VARDECL_PARAM; break; default: + if (token_is_keyword(c->tok)) + { + RETURN_PRINT_ERROR_HERE("'%s' is a keyword and cannot be used as a parameter name.", symstr(c)); + } RETURN_PRINT_ERROR_HERE("Expected a parameter."); } if (type && type->optional) @@ -2280,10 +2284,14 @@ static inline bool parse_func_macro_header(ParseContext *c, Decl *decl) } // 2. Now we must have a type - either that is the return type or the method type. + if (is_macro && token_is_keyword(c->tok) && c->lexer.token_type == TOKEN_LPAREN) + { + RETURN_PRINT_ERROR_HERE("This is a reserved keyword and can't be used as a macro name."); + } ASSIGN_TYPE_OR_RET(rtype, parse_optional_type(c), false); // 4. We might have a type here, if so then we read it. - if (!tok_is(c, TOKEN_DOT) && !parse_is_macro_name(c)) + if (!tok_is(c, TOKEN_DOT) && !parse_is_macro_name(c) && !token_is_keyword(c->tok)) { ASSIGN_TYPE_OR_RET(method_type, parse_type(c), false); } @@ -2320,6 +2328,11 @@ static inline bool parse_func_macro_header(ParseContext *c, Decl *decl) RESULT: decl->name = symstr(c); decl->span = c->span; + if (token_is_keyword(c->tok) && c->lexer.token_type == TOKEN_LPAREN) + { + RETURN_PRINT_ERROR_HERE("This is a reserved keyword and can't be used as a %s name.", is_macro ? "macro" : "function"); + } + if (is_macro && c->tok != TOKEN_IDENT && c->tok != TOKEN_AT_IDENT) { print_error_at(c->span, "Expected a macro name here, e.g. '@someName' or 'someName'."); diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index 34c7541a5..57f9f8fbc 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -144,6 +144,11 @@ INLINE bool token_is_keyword_ident(TokenType token_type) return token_type >= TOKEN_FIRST_KEYWORD && token_type <= TOKEN_LAST_NON_CT_KEYWORD; } +INLINE bool token_is_keyword(TokenType token_type) +{ + return token_type >= TOKEN_FIRST_NON_TYPE_KEYWORD && token_type <= TOKEN_LAST_NON_CT_KEYWORD; +} + static inline bool expect_ident(ParseContext *c, const char* name) { switch (c->tok) diff --git a/test/test_suite/functions/function_parse_errors.c3 b/test/test_suite/functions/function_parse_errors.c3 new file mode 100644 index 000000000..f0de1217d --- /dev/null +++ b/test/test_suite/functions/function_parse_errors.c3 @@ -0,0 +1,10 @@ +macro continue(var) // #error: This is a reserved +{ +} + +fn int continue(int x) // #error: This is a reserved +{ + return 0; +} + +fn int do_continue(var) {} // #error: 'var' is a keyword \ No newline at end of file