Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/debug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ std::string debugTokenName(TokenType t) {
{tok_phpdoc, "tok_phpdoc"},
{tok_clone, "tok_clone"},
{tok_instanceof, "tok_instanceof"},
{tok_attribute_start, "tok_attribute_start"},
{tok_end, "tok_end"},
});

Expand Down
52 changes: 52 additions & 0 deletions compiler/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,10 @@ VertexPtr GenTree::get_def_value() {
VertexAdaptor<op_func_param> GenTree::get_func_param() {
auto location = auto_location();

if (test_expect(tok_attribute_start)) {
get_attribute_group();
}

const TypeHint *type_hint = get_typehint();
bool is_varg = false;

Expand Down Expand Up @@ -2139,6 +2143,8 @@ VertexPtr GenTree::get_statement(vk::string_view phpdoc_str) {
return get_foreach();
case tok_switch:
return get_switch();
case tok_attribute_start:
return get_attribute_group();
case tok_phpdoc: { // enter /** ... */
// is it a function/class/field phpdoc?
auto next = (cur+1)->type();
Expand Down Expand Up @@ -2233,6 +2239,52 @@ VertexPtr GenTree::get_statement(vk::string_view phpdoc_str) {
kphp_fail();
}

VertexAdaptor<op_empty> GenTree::get_attribute_group() {
get_attribute();
while (test_expect(tok_attribute_start)) {
get_attribute();
}
return VertexAdaptor<op_empty>::create();
}

VertexAdaptor<op_empty> GenTree::get_attribute() {
next_cur();
auto calls = std::vector<VertexAdaptor<op_func_call>>();
const auto ok = gen_list<op_none>(&calls, &GenTree::get_attribute_call, tok_comma);
CE(!kphp_error(ok, "Failed to parse attribute call"));
CE(expect(tok_clbrk, "']'"));

return VertexAdaptor<op_empty>::create();
}

VertexAdaptor<op_func_call> GenTree::get_attribute_call() {
const auto location = auto_location();
if (cur->type() == tok_clbrk) {
return {};
}

const auto name = std::string(cur->str_val);
next_cur();

VertexAdaptor<op_func_call> call;

if (test_expect(tok_oppar)) {
CE (expect(tok_oppar, "'('"));
skip_phpdoc_tokens();
vector<VertexPtr> next;
bool ok_next = gen_list<op_none>(&next, &GenTree::get_expression, tok_comma);
CE (!kphp_error(ok_next, "get argument list failed"));
CE (expect(tok_clpar, "')'"));

call = VertexAdaptor<op_func_call>::create_vararg(next).set_location(location);
}

call = VertexAdaptor<op_func_call>::create_vararg().set_location(location);

call->set_string(name);
return call;
}

VertexPtr GenTree::get_const_after_explicit_access_modifier(AccessModifiers access) {
CE(!kphp_error(cur_class, "Access modifier for const allowed in class only"));
if (cur_class->is_interface()) {
Expand Down
4 changes: 4 additions & 0 deletions compiler/gentree.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ class GenTree {
VertexAdaptor<op_func_call> get_anonymous_function(TokenType tok = tok_function, bool is_static = false);
VertexAdaptor<op_function> get_function(TokenType tok, vk::string_view phpdoc_str, FunctionModifiers modifiers, std::vector<VertexAdaptor<op_func_param>> *uses_of_lambda = nullptr);

VertexAdaptor<op_empty> get_attribute_group();
VertexAdaptor<op_empty> get_attribute();
VertexAdaptor<op_func_call> get_attribute_call();

ClassMemberModifiers parse_class_member_modifier_mask();
VertexPtr get_class_member(vk::string_view phpdoc_str);

Expand Down
6 changes: 6 additions & 0 deletions compiler/lexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,12 @@ bool TokenLexerComment::parse(LexerData *lexer_data) const {
const char *s = lexer_data->get_code(),
*st = s;

if (s[0] == '#' && s[1] == '[') {
lexer_data->add_token(0, tok_attribute_start, s, s+2);
lexer_data->pass(2);
return vk::singleton<TokenLexerPHP>::get().parse(lexer_data);
}

assert (s[0] == '/' || s[0] == '#');
if (s[0] == '#' || s[1] == '/') {
while (s[0] && s[0] != '\n') {
Expand Down
4 changes: 2 additions & 2 deletions compiler/phpdoc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ const TypeHint *PhpDocTypeRuleParser::parse_type_expression() {
bool was_raw_bool = false;
bool was_false = false;
bool was_null = false;

auto on_each_item = [&](const TypeHint *item) {
items.emplace_back(item);
if (const auto *as_primitive = item->try_as<TypeHintPrimitive>()) {
Expand All @@ -457,7 +457,7 @@ const TypeHint *PhpDocTypeRuleParser::parse_type_expression() {
}
}

// try to simplify: T|false and similar as an optional<T>, not as a vector
// try to simplify: T|false and similar to an optional<T>, not as a vector
bool can_be_simplified_as_optional = 1 == (items.size() - was_false - was_null);
if (can_be_simplified_as_optional) {
for (const TypeHint *item : items) {
Expand Down
2 changes: 2 additions & 0 deletions compiler/token.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ enum TokenType {
tok_clone,
tok_instanceof,

tok_attribute_start,

tok_end
};

Expand Down
7 changes: 7 additions & 0 deletions tests/cpp/compiler/lexer-test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,13 @@ TEST(lexer_test, test_php_tokens) {

// combined tests
{"echo \"{$x->y}\";", {"tok_echo(echo)", "tok_str_begin(\")", "tok_expr_begin({)", "tok_var_name($x)", "tok_arrow(->)", "tok_func_name(y)", "tok_expr_end(})", "tok_str_end(\")", "tok_semicolon(;)"}},

// attributes
{"#[Attribute]", {"tok_attribute_start(#[)", "tok_func_name(Attribute)", "tok_clbrk(])"}},
{"#[\nAttribute\n]", {"tok_attribute_start(#[)", "tok_func_name(Attribute)", "tok_clbrk(])"}},
{"#[Attribute()]", {"tok_attribute_start(#[)", "tok_func_name(Attribute)", "tok_oppar(()", "tok_clpar())", "tok_clbrk(])"}},
{"#[Attribute('name')]", {"tok_attribute_start(#[)", "tok_func_name(Attribute)", "tok_oppar(()", "tok_str(name)", "tok_clpar())", "tok_clbrk(])"}},
{"#[Attribute(), Attribute2()]", {"tok_attribute_start(#[)", "tok_func_name(Attribute)", "tok_oppar(()", "tok_clpar())", "tok_comma(,)", "tok_func_name(Attribute2)", "tok_oppar(()", "tok_clpar())", "tok_clbrk(])"}},
};

for (const auto &test : tests) {
Expand Down
37 changes: 37 additions & 0 deletions tests/phpt/php8/attributes/001_class_attributes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
@ok php8
<?php

class Boo {}
interface Goo {}
interface Doo {}

#[SimpleAttribute]
class Foo1 {}

#[AttributeWithArgs(100, SomeClass::class)]
class Foo2 extends Boo {}

#[FirstAttribute(100, SomeClass::class), SecondAttribute()]
class Foo3 implements Goo {}

#[FirstAttribute(100, SomeClass::class), SecondAttribute()]
abstract class Foo4 implements Goo, Doo {}

#[
FirstLineAttribute(100, SomeClass::class),
SecondLineAttribute()
]
class Foo5 {}

#[FirstAttribute(100, SomeClass::class), SecondAttribute()]
#[SecondGroupFirstAttribute(SomeClass::class), SecondGroupSecondAttribute]
class Foo6 extends Boo implements Goo {}

#[\WithSlash]
class Foo7 {}

#[\F\Q\N]
class Foo8 {}

#[\WithSlash]
final class Foo9 extends Boo implements Goo, Doo {}
31 changes: 31 additions & 0 deletions tests/phpt/php8/attributes/002_interface_attributes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
@ok php8
<?php

interface Boo {}
interface Goo {}
interface Doo {}

#[SimpleAttribute]
interface Foo1 {}

#[AttributeWithArgs(100, SomeClass::class)]
interface Foo2 extends Boo {}

#[FirstAttribute(100, SomeClass::class), SecondAttribute()]
interface Foo3 {}

#[
FirstLineAttribute(100, SomeClass::class),
SecondLineAttribute()
]
interface Foo4 {}

#[FirstAttribute(100, SomeClass::class), SecondAttribute()]
#[SecondGroupFirstAttribute(SomeClass::class), SecondGroupSecondAttribute]
interface Foo5 extends Boo, Goo {}

#[\WithSlash]
interface Foo6 {}

#[\F\Q\N]
interface Foo7 {}
25 changes: 25 additions & 0 deletions tests/phpt/php8/attributes/003_function_attributes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
@ok php8
<?php

#[AttributeWithArgs(100, SomeClass::class)]
function f1() {}

#[FirstAttribute(100, SomeClass::class), SecondAttribute()]
function f2(): void {}

#[
FirstLineAttribute(100, SomeClass::class),
SecondLineAttribute(),
]
function f3() {}

#[FirstAttribute(100, SomeClass::class), SecondAttribute()]
#[SecondGroupFirstAttribute(SomeClass::class), SecondGroupSecondAttribute]
function f4(): int|string {}

#[\WithSlash]
function f5() {}

#[\F\Q\N]
function f6(): \Foo|string {}

53 changes: 53 additions & 0 deletions tests/phpt/php8/attributes/004_param_attributes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
@ok php8
<?php

class Foo {
public function f1(#[SimpleAttribute] string|int $a) {}
public function f2(
#[SimpleAttribute] string|int $a,
#[AttributeWithArgs(100, SomeClass::class)] string|int $b,
#[
FirstLineAttribute(100, SomeClass::class),
SecondLineAttribute()
]
$c,
#[FirstAttribute(100, SomeClass::class), SecondAttribute()]
#[SecondGroupFirstAttribute(SomeClass::class), SecondGroupSecondAttribute]
Foo|string $d = "100",
#[
FirstAttribute(100, SomeClass::class),
SecondAttribute(),
]
#[
SecondGroupFirstAttribute(SomeClass::class),
SecondGroupSecondAttribute
]
$e = 100,
#[\F\Q\N] string|int $f = 100
) {}
}

function f1(#[SimpleAttribute] $a) {}

function f2(
#[SimpleAttribute] string|int $a,
#[AttributeWithArgs(100, SomeClass::class)] string|int $b,
#[
FirstLineAttribute(100, SomeClass::class),
SecondLineAttribute()
]
$c,
#[FirstAttribute(100, SomeClass::class), SecondAttribute()]
#[SecondGroupFirstAttribute(SomeClass::class), SecondGroupSecondAttribute]
\Foo|string $d = "100",
#[
FirstAttribute(100, SomeClass::class),
SecondAttribute(),
]
#[
SecondGroupFirstAttribute(SomeClass::class),
SecondGroupSecondAttribute
]
$e = 100,
#[\F\Q\N] string|int $f = 100
) {}
32 changes: 32 additions & 0 deletions tests/phpt/php8/attributes/005_property_attributes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@ok php8
<?php

class Foo {
#[SimpleAttribute]
public $a1 = "";

#[AttributeWithArgs(100, SomeClass::class)]
protected $a2 = null;

#[FirstAttribute(100, SomeClass::class), SecondAttribute()]
var $a3 = null, $b1 = 100;

#[FirstAttribute(100, SomeClass::class), SecondAttribute()]
private ?Foo $a4 = null;

#[
FirstLineAttribute(100, SomeClass::class),
SecondLineAttribute()
]
public int|string $a5 = 10, $b, $c, $d;

#[FirstAttribute(100, SomeClass::class), SecondAttribute()]
#[SecondGroupFirstAttribute(SomeClass::class), SecondGroupSecondAttribute]
var $a6 = 100;

#[\WithSlash]
public $a7 = null;

#[\F\Q\N]
protected $a8 = null;
}
32 changes: 32 additions & 0 deletions tests/phpt/php8/attributes/006_constant_attributes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@ok php8
<?php

class Foo {
#[SimpleAttribute]
public const A = 100;

#[AttributeWithArgs(100, SomeClass::class)]
private const A1 = 10;

#[FirstAttribute(100, SomeClass::class), SecondAttribute()]
protected const A2 = "a";

#[FirstAttribute(100, SomeClass::class), SecondAttribute()]
public const A3 = 100;

#[
FirstLineAttribute(100, SomeClass::class),
SecondLineAttribute()
]
public const A4 = 100;

#[FirstAttribute(100, SomeClass::class), SecondAttribute()]
#[SecondGroupFirstAttribute(SomeClass::class), SecondGroupSecondAttribute]
public const A5 = 100;

#[\WithSlash]
protected const A6 = 100;

#[\F\Q\N]
public const A7 = 100;
}