From cb02a1881da54047c51e87bc1de284081532754b Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 15 Jun 2020 11:50:55 +0200 Subject: [PATCH 1/2] Treat namespaced names as single token --- .../019_variable_attribute_name.phpt | 2 +- Zend/tests/bug43343.phpt | 2 +- Zend/tests/bug55086.phpt | 2 +- Zend/tests/grammar/regression_010.phpt | 7 ++- Zend/tests/namespace_name_namespace.phpt | 10 ++++ .../tests/namespace_name_namespace_start.phpt | 10 ++++ .../namespace_name_reserved_keywords.phpt | 37 +++++++++++++++ Zend/tests/namespaced_name_whitespace.phpt | 10 ++++ Zend/tests/ns_096.phpt | 2 +- Zend/tests/ns_trailing_comma_error_01.phpt | 2 +- Zend/tests/ns_trailing_comma_error_02.phpt | 2 +- Zend/tests/ns_trailing_comma_error_04.phpt | 2 +- Zend/tests/ns_trailing_comma_error_07.phpt | 2 +- Zend/tests/special_name_error1.phpt | 10 ---- Zend/zend_compile.c | 2 +- Zend/zend_language_parser.y | 47 ++++++++++++------- Zend/zend_language_scanner.l | 20 ++++++-- 17 files changed, 128 insertions(+), 41 deletions(-) create mode 100644 Zend/tests/namespace_name_namespace.phpt create mode 100644 Zend/tests/namespace_name_namespace_start.phpt create mode 100644 Zend/tests/namespace_name_reserved_keywords.phpt create mode 100644 Zend/tests/namespaced_name_whitespace.phpt delete mode 100644 Zend/tests/special_name_error1.phpt diff --git a/Zend/tests/attributes/019_variable_attribute_name.phpt b/Zend/tests/attributes/019_variable_attribute_name.phpt index 2710abbed9f94..a259c06f3fe77 100644 --- a/Zend/tests/attributes/019_variable_attribute_name.phpt +++ b/Zend/tests/attributes/019_variable_attribute_name.phpt @@ -8,4 +8,4 @@ class A {} ?> --EXPECTF-- -Parse error: syntax error, unexpected variable "$x", expecting identifier or "static" or "namespace" or "\" in %s on line %d +Parse error: syntax error, unexpected variable "$x" in %s on line %d diff --git a/Zend/tests/bug43343.phpt b/Zend/tests/bug43343.phpt index db72fe98ecfa7..c277ccccddcdb 100644 --- a/Zend/tests/bug43343.phpt +++ b/Zend/tests/bug43343.phpt @@ -8,4 +8,4 @@ $foo = 'bar'; var_dump(new namespace::$foo); ?> --EXPECTF-- -Parse error: %s error%sexpecting%s"\"%sin %sbug43343.php on line 5 +Parse error: syntax error, unexpected token "namespace" in %s on line %d diff --git a/Zend/tests/bug55086.phpt b/Zend/tests/bug55086.phpt index 9a5c747b9f007..10be0d1ef512a 100644 --- a/Zend/tests/bug55086.phpt +++ b/Zend/tests/bug55086.phpt @@ -26,7 +26,7 @@ namespace N2 { echo $a->hello(), PHP_EOL; echo $a->foo(), PHP_EOL; try { - } catch(namespace \Foo $e) + } catch (namespace\Foo $e) { } } diff --git a/Zend/tests/grammar/regression_010.phpt b/Zend/tests/grammar/regression_010.phpt index e5e65e3a85d8d..0e535aad6b87f 100644 --- a/Zend/tests/grammar/regression_010.phpt +++ b/Zend/tests/grammar/regression_010.phpt @@ -5,8 +5,11 @@ Test to check regressions on T_IMPLEMENTS followed by a T_NS_SEPARATOR interface A{} +// No longer considered legal in PHP 8. class B implements\A {} echo "Done", PHP_EOL; ---EXPECT-- -Done + +?> +--EXPECTF-- +Parse error: syntax error, unexpected namespaced name "implements\A", expecting "{" in %s on line %d diff --git a/Zend/tests/namespace_name_namespace.phpt b/Zend/tests/namespace_name_namespace.phpt new file mode 100644 index 0000000000000..311705ab2e2e1 --- /dev/null +++ b/Zend/tests/namespace_name_namespace.phpt @@ -0,0 +1,10 @@ +--TEST-- +Cannot use "namespace" as namespace name, due to conflict with ns-relative names +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use 'NAMEspace' as namespace name in %s on line %d diff --git a/Zend/tests/namespace_name_namespace_start.phpt b/Zend/tests/namespace_name_namespace_start.phpt new file mode 100644 index 0000000000000..bf812fed79495 --- /dev/null +++ b/Zend/tests/namespace_name_namespace_start.phpt @@ -0,0 +1,10 @@ +--TEST-- +Cannot use "namespace\xyz" as namespace name, due to conflict with ns-relative names +--FILE-- + +--EXPECTF-- +Parse error: syntax error, unexpected namespace-relative name "NAMEspace\xyz", expecting "{" in %s on line %d diff --git a/Zend/tests/namespace_name_reserved_keywords.phpt b/Zend/tests/namespace_name_reserved_keywords.phpt new file mode 100644 index 0000000000000..9374ef6a63dbd --- /dev/null +++ b/Zend/tests/namespace_name_reserved_keywords.phpt @@ -0,0 +1,37 @@ +--TEST-- +Reserved keywords in namespace name +--FILE-- + +--EXPECT-- +iter\fn\test +fn\test +self\test diff --git a/Zend/tests/namespaced_name_whitespace.phpt b/Zend/tests/namespaced_name_whitespace.phpt new file mode 100644 index 0000000000000..746b3298e9168 --- /dev/null +++ b/Zend/tests/namespaced_name_whitespace.phpt @@ -0,0 +1,10 @@ +--TEST-- +Whitespace between namespace separators is no longer allowed +--FILE-- + +--EXPECTF-- +Parse error: syntax error, unexpected token "\" in %s on line %d diff --git a/Zend/tests/ns_096.phpt b/Zend/tests/ns_096.phpt index f72e37f7d3b1f..03b09047ff49b 100644 --- a/Zend/tests/ns_096.phpt +++ b/Zend/tests/ns_096.phpt @@ -7,4 +7,4 @@ use Foo\Bar\{\Baz}; ?> --EXPECTF-- -Parse error: syntax error, unexpected token "\", expecting identifier or "function" or "const" in %s on line %d +Parse error: syntax error, unexpected fully qualified name "\Baz", expecting identifier or namespaced name or "function" or "const" in %s on line %d diff --git a/Zend/tests/ns_trailing_comma_error_01.phpt b/Zend/tests/ns_trailing_comma_error_01.phpt index 7b9c5177bf1dc..f8fa5dbf1218c 100644 --- a/Zend/tests/ns_trailing_comma_error_01.phpt +++ b/Zend/tests/ns_trailing_comma_error_01.phpt @@ -5,4 +5,4 @@ Group use declarations mustn't be empty use Baz\{}; ?> --EXPECTF-- -Parse error: syntax error, unexpected token "}", expecting identifier or "function" or "const" in %s on line %d +Parse error: syntax error, unexpected token "}", expecting identifier or namespaced name or "function" or "const" in %s on line %d diff --git a/Zend/tests/ns_trailing_comma_error_02.phpt b/Zend/tests/ns_trailing_comma_error_02.phpt index 91b70d3bd640f..f73ffe748d94b 100644 --- a/Zend/tests/ns_trailing_comma_error_02.phpt +++ b/Zend/tests/ns_trailing_comma_error_02.phpt @@ -5,4 +5,4 @@ Group use declarations mustn't contain just a comma use Baz\{,}; ?> --EXPECTF-- -Parse error: syntax error, unexpected token ",", expecting identifier or "function" or "const" in %s on line %d +Parse error: syntax error, unexpected token ",", expecting identifier or namespaced name or "function" or "const" in %s on line %d diff --git a/Zend/tests/ns_trailing_comma_error_04.phpt b/Zend/tests/ns_trailing_comma_error_04.phpt index 2712c5aa115c4..18bbea745a323 100644 --- a/Zend/tests/ns_trailing_comma_error_04.phpt +++ b/Zend/tests/ns_trailing_comma_error_04.phpt @@ -5,4 +5,4 @@ Group use declarations mustn't begin with a comma use Baz\{,Foo}; ?> --EXPECTF-- -Parse error: syntax error, unexpected token ",", expecting identifier or "function" or "const" in %s on line %d +Parse error: syntax error, unexpected token ",", expecting identifier or namespaced name or "function" or "const" in %s on line %d diff --git a/Zend/tests/ns_trailing_comma_error_07.phpt b/Zend/tests/ns_trailing_comma_error_07.phpt index 742d809bbc9ca..c7798da8f072e 100644 --- a/Zend/tests/ns_trailing_comma_error_07.phpt +++ b/Zend/tests/ns_trailing_comma_error_07.phpt @@ -5,4 +5,4 @@ Unmixed group use declarations mustn't begin with a comma use function Baz\{,Foo}; ?> --EXPECTF-- -Parse error: syntax error, unexpected token ",", expecting identifier in %s on line %d +Parse error: syntax error, unexpected token ",", expecting identifier or namespaced name in %s on line %d diff --git a/Zend/tests/special_name_error1.phpt b/Zend/tests/special_name_error1.phpt deleted file mode 100644 index 63bf5a63fd387..0000000000000 --- a/Zend/tests/special_name_error1.phpt +++ /dev/null @@ -1,10 +0,0 @@ ---TEST-- -Cannot use special class name as namespace ---FILE-- - ---EXPECTF-- -Fatal error: Cannot use 'self' as namespace name in %s on line %d diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index aafe582f9f551..d555297c17d89 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -7432,7 +7432,7 @@ void zend_compile_namespace(zend_ast *ast) /* {{{ */ if (name_ast) { name = zend_ast_get_str(name_ast); - if (ZEND_FETCH_CLASS_DEFAULT != zend_get_class_fetch_type(name)) { + if (zend_string_equals_literal_ci(name, "namespace")) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use '%s' as namespace name", ZSTR_VAL(name)); } diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 891624c461f67..acb7a3d954ee2 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -89,6 +89,9 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %token T_LNUMBER "integer" %token T_DNUMBER "floating-point number" %token T_STRING "identifier" +%token T_NAME_FULLY_QUALIFIED "fully qualified name" +%token T_NAME_RELATIVE "namespace-relative name" +%token T_NAME_QUALIFIED "namespaced name" %token T_VARIABLE "variable" %token T_INLINE_HTML %token T_ENCAPSED_AND_WHITESPACE "string content" @@ -231,7 +234,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %token T_ERROR %type top_statement namespace_name name statement function_declaration_statement -%type class_declaration_statement trait_declaration_statement +%type class_declaration_statement trait_declaration_statement legacy_namespace_name %type interface_declaration_statement interface_extends_list %type group_use_declaration inline_use_declarations inline_use_declaration %type mixed_group_use_declaration use_declaration unprefixed_use_declaration @@ -261,7 +264,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type identifier type_expr_without_static union_type_without_static %type inline_function union_type %type attributed_statement attributed_class_statement attributed_parameter -%type attribute_decl attribute attributes +%type attribute_decl attribute attributes namespace_declaration_name %type match match_arm_list non_empty_match_arm_list match_arm match_arm_cond_list %type returns_ref function fn is_reference is_variadic variable_modifiers @@ -308,15 +311,29 @@ top_statement_list: | %empty { $$ = zend_ast_create_list(0, ZEND_AST_STMT_LIST); } ; +/* Name usable in a namespace declaration. */ +namespace_declaration_name: + identifier { $$ = $1; } + | T_NAME_QUALIFIED { $$ = $1; } +; + +/* Name usable in "use" declarations (loading separator forbidden). */ namespace_name: T_STRING { $$ = $1; } - | namespace_name T_NS_SEPARATOR T_STRING { $$ = zend_ast_append_str($1, $3); } + | T_NAME_QUALIFIED { $$ = $1; } +; + +/* Name usable in "use" declarations (leading separator allowed). */ +legacy_namespace_name: + namespace_name { $$ = $1; } + | T_NAME_FULLY_QUALIFIED { $$ = $1; } ; name: - namespace_name { $$ = $1; $$->attr = ZEND_NAME_NOT_FQ; } - | T_NAMESPACE T_NS_SEPARATOR namespace_name { $$ = $3; $$->attr = ZEND_NAME_RELATIVE; } - | T_NS_SEPARATOR namespace_name { $$ = $2; $$->attr = ZEND_NAME_FQ; } + T_STRING { $$ = $1; $$->attr = ZEND_NAME_NOT_FQ; } + | T_NAME_QUALIFIED { $$ = $1; $$->attr = ZEND_NAME_NOT_FQ; } + | T_NAME_FULLY_QUALIFIED { $$ = $1; $$->attr = ZEND_NAME_FQ; } + | T_NAME_RELATIVE { $$ = $1; $$->attr = ZEND_NAME_RELATIVE; } ; attribute_decl: @@ -350,10 +367,10 @@ top_statement: { $$ = zend_ast_create(ZEND_AST_HALT_COMPILER, zend_ast_create_zval_from_long(zend_get_scanned_file_offset())); zend_stop_lexing(); } - | T_NAMESPACE namespace_name ';' + | T_NAMESPACE namespace_declaration_name ';' { $$ = zend_ast_create(ZEND_AST_NAMESPACE, $2, NULL); RESET_DOC_COMMENT(); } - | T_NAMESPACE namespace_name { RESET_DOC_COMMENT(); } + | T_NAMESPACE namespace_declaration_name { RESET_DOC_COMMENT(); } '{' top_statement_list '}' { $$ = zend_ast_create(ZEND_AST_NAMESPACE, $2, $5); } | T_NAMESPACE { RESET_DOC_COMMENT(); } @@ -372,17 +389,13 @@ use_type: ; group_use_declaration: - namespace_name T_NS_SEPARATOR '{' unprefixed_use_declarations possible_comma '}' + legacy_namespace_name T_NS_SEPARATOR '{' unprefixed_use_declarations possible_comma '}' { $$ = zend_ast_create(ZEND_AST_GROUP_USE, $1, $4); } - | T_NS_SEPARATOR namespace_name T_NS_SEPARATOR '{' unprefixed_use_declarations possible_comma '}' - { $$ = zend_ast_create(ZEND_AST_GROUP_USE, $2, $5); } ; mixed_group_use_declaration: - namespace_name T_NS_SEPARATOR '{' inline_use_declarations possible_comma '}' + legacy_namespace_name T_NS_SEPARATOR '{' inline_use_declarations possible_comma '}' { $$ = zend_ast_create(ZEND_AST_GROUP_USE, $1, $4);} - | T_NS_SEPARATOR namespace_name T_NS_SEPARATOR '{' inline_use_declarations possible_comma '}' - { $$ = zend_ast_create(ZEND_AST_GROUP_USE, $2, $5); } ; possible_comma: @@ -424,8 +437,10 @@ unprefixed_use_declaration: ; use_declaration: - unprefixed_use_declaration { $$ = $1; } - | T_NS_SEPARATOR unprefixed_use_declaration { $$ = $2; } + legacy_namespace_name + { $$ = zend_ast_create(ZEND_AST_USE_ELEM, $1, NULL); } + | legacy_namespace_name T_AS T_STRING + { $$ = zend_ast_create(ZEND_AST_USE_ELEM, $1, $3); } ; const_list: diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index b6cd505a633aa..2ebe92e6bd4bb 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -1596,10 +1596,6 @@ NEWLINE ("\r"|"\n"|"\r\n") RETURN_TOKEN(T_PAAMAYIM_NEKUDOTAYIM); } -"\\" { - RETURN_TOKEN(T_NS_SEPARATOR); -} - "..." { RETURN_TOKEN(T_ELLIPSIS); } @@ -2288,6 +2284,22 @@ inline_char_handler: RETURN_TOKEN_WITH_VAL(T_ENCAPSED_AND_WHITESPACE); } +"namespace"("\\"{LABEL})+ { + RETURN_TOKEN_WITH_STR(T_NAME_RELATIVE, sizeof("namespace\\") - 1); +} + +{LABEL}("\\"{LABEL})+ { + RETURN_TOKEN_WITH_STR(T_NAME_QUALIFIED, 0); +} + +"\\"{LABEL}("\\"{LABEL})* { + RETURN_TOKEN_WITH_STR(T_NAME_FULLY_QUALIFIED, 1); +} + +"\\" { + RETURN_TOKEN(T_NS_SEPARATOR); +} + {LABEL} { RETURN_TOKEN_WITH_STR(T_STRING, 0); } From ad37f01e0890506f515d3c3c39dbf6ad5cde1542 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 22 Jul 2020 11:41:12 +0200 Subject: [PATCH 2/2] Generate tokenizer data and add test --- ext/tokenizer/tests/namespaced_names.phpt | 39 +++++++++++++++++++++++ ext/tokenizer/tokenizer_data.c | 6 ++++ 2 files changed, 45 insertions(+) create mode 100644 ext/tokenizer/tests/namespaced_names.phpt diff --git a/ext/tokenizer/tests/namespaced_names.phpt b/ext/tokenizer/tests/namespaced_names.phpt new file mode 100644 index 0000000000000..34e947b3525e1 --- /dev/null +++ b/ext/tokenizer/tests/namespaced_names.phpt @@ -0,0 +1,39 @@ +--TEST-- +Tokenization of namespaced names +--FILE-- +getTokenName()}: \"$token->text\"\n"; +} + +?> +--EXPECT-- +T_OPEN_TAG: "