Rollup merge of #68764 - Centril:self-semantic, r=petrochenkov
parser: syntactically allow `self` in all `fn` contexts Part of https://github.com/rust-lang/rust/pull/68728. `self` parameters are now *syntactically* allowed as the first parameter irrespective of item context (and in function pointers). Instead, semantic validation (`ast_validation`) is used. r? @petrochenkov
This commit is contained in:
commit
5951cd3dda
20 changed files with 453 additions and 87 deletions
|
|
@ -1,6 +1,6 @@
|
|||
fn a(&self) { }
|
||||
//~^ ERROR unexpected `self` parameter in function
|
||||
//~| NOTE not valid as function parameter
|
||||
//~| NOTE `self` is only valid as the first parameter of an associated function
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
//~| NOTE not semantically valid as function parameter
|
||||
//~| NOTE associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
fn main() { }
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
error: unexpected `self` parameter in function
|
||||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/bare-fn-start.rs:1:6
|
||||
|
|
||||
LL | fn a(&self) { }
|
||||
| ^^^^^ not valid as function parameter
|
||||
| ^^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: `self` is only valid as the first parameter of an associated function
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
fn b(foo: u32, &mut self) { }
|
||||
//~^ ERROR unexpected `self` parameter in function
|
||||
//~| NOTE not valid as function parameter
|
||||
//~| NOTE `self` is only valid as the first parameter of an associated function
|
||||
//~| NOTE must be the first parameter of an associated function
|
||||
|
||||
fn main() { }
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@ error: unexpected `self` parameter in function
|
|||
--> $DIR/bare-fn.rs:1:16
|
||||
|
|
||||
LL | fn b(foo: u32, &mut self) { }
|
||||
| ^^^^^^^^^ not valid as function parameter
|
||||
|
|
||||
= note: `self` is only valid as the first parameter of an associated function
|
||||
| ^^^^^^^^^ must be the first parameter of an associated function
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ struct Foo {}
|
|||
impl Foo {
|
||||
fn c(foo: u32, self) {}
|
||||
//~^ ERROR unexpected `self` parameter in function
|
||||
//~| NOTE must be the first associated function parameter
|
||||
//~| NOTE must be the first parameter of an associated function
|
||||
|
||||
fn good(&mut self, foo: u32) {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ error: unexpected `self` parameter in function
|
|||
--> $DIR/trait-fn.rs:4:20
|
||||
|
|
||||
LL | fn c(foo: u32, self) {}
|
||||
| ^^^^ must be the first associated function parameter
|
||||
| ^^^^ must be the first parameter of an associated function
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ fn pattern((i32, i32) (a, b)) {}
|
|||
fn fizz(i32) {}
|
||||
//~^ ERROR expected one of `:`, `@`
|
||||
//~| HELP if this was a parameter name, give it a type
|
||||
//~| HELP if this is a `self` type, give it a parameter name
|
||||
//~| HELP if this is a type, explicitly ignore the parameter name
|
||||
|
||||
fn missing_colon(quux S) {}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,10 @@ LL | fn fizz(i32) {}
|
|||
| ^ expected one of `:`, `@`, or `|`
|
||||
|
|
||||
= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
|
||||
help: if this is a `self` type, give it a parameter name
|
||||
|
|
||||
LL | fn fizz(self: i32) {}
|
||||
| ^^^^^^^^^
|
||||
help: if this was a parameter name, give it a type
|
||||
|
|
||||
LL | fn fizz(i32: TypeName) {}
|
||||
|
|
@ -45,7 +49,7 @@ LL | fn fizz(_: i32) {}
|
|||
| ^^^^^^
|
||||
|
||||
error: expected one of `:`, `@`, or `|`, found `S`
|
||||
--> $DIR/inverted-parameters.rs:26:23
|
||||
--> $DIR/inverted-parameters.rs:27:23
|
||||
|
|
||||
LL | fn missing_colon(quux S) {}
|
||||
| -----^
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ LL | fn foo(x) {
|
|||
| ^ expected one of `:`, `@`, or `|`
|
||||
|
|
||||
= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
|
||||
help: if this is a `self` type, give it a parameter name
|
||||
|
|
||||
LL | fn foo(self: x) {
|
||||
| ^^^^^^^
|
||||
help: if this was a parameter name, give it a type
|
||||
|
|
||||
LL | fn foo(x: TypeName) {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ LL | fn a(B<) {}
|
|||
| ^ expected one of `:`, `@`, or `|`
|
||||
|
|
||||
= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
|
||||
help: if this is a `self` type, give it a parameter name
|
||||
|
|
||||
LL | fn a(self: B<) {}
|
||||
| ^^^^^^^
|
||||
help: if this is a type, explicitly ignore the parameter name
|
||||
|
|
||||
LL | fn a(_: B<) {}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@ error: unexpected `self` parameter in function
|
|||
--> $DIR/self-in-function-arg.rs:1:15
|
||||
|
|
||||
LL | fn foo(x:i32, self: i32) -> i32 { self }
|
||||
| ^^^^ not valid as function parameter
|
||||
|
|
||||
= note: `self` is only valid as the first parameter of an associated function
|
||||
| ^^^^ must be the first parameter of an associated function
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
|
|||
64
src/test/ui/parser/self-param-semantic-fail.rs
Normal file
64
src/test/ui/parser/self-param-semantic-fail.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
// This test ensures that `self` is semantically rejected
|
||||
// in contexts with `FnDecl` but outside of associated `fn`s.
|
||||
// FIXME(Centril): For now closures are an exception.
|
||||
|
||||
fn main() {}
|
||||
|
||||
fn free() {
|
||||
fn f1(self) {}
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
fn f2(mut self) {}
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
fn f3(&self) {}
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
fn f4(&mut self) {}
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
fn f5<'a>(&'a self) {}
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
fn f6<'a>(&'a mut self) {}
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
fn f7(self: u8) {}
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
fn f8(mut self: u8) {}
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
}
|
||||
|
||||
extern {
|
||||
fn f1(self);
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
fn f2(mut self);
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
//~| ERROR patterns aren't allowed in
|
||||
fn f3(&self);
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
fn f4(&mut self);
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
fn f5<'a>(&'a self);
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
fn f6<'a>(&'a mut self);
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
fn f7(self: u8);
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
fn f8(mut self: u8);
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
//~| ERROR patterns aren't allowed in
|
||||
}
|
||||
|
||||
type X1 = fn(self);
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
type X2 = fn(mut self);
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
//~| ERROR patterns aren't allowed in
|
||||
type X3 = fn(&self);
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
type X4 = fn(&mut self);
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
type X5 = for<'a> fn(&'a self);
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
type X6 = for<'a> fn(&'a mut self);
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
type X7 = fn(self: u8);
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
type X8 = fn(mut self: u8);
|
||||
//~^ ERROR `self` parameter is only allowed in associated functions
|
||||
//~| ERROR patterns aren't allowed in
|
||||
220
src/test/ui/parser/self-param-semantic-fail.stderr
Normal file
220
src/test/ui/parser/self-param-semantic-fail.stderr
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/self-param-semantic-fail.rs:8:11
|
||||
|
|
||||
LL | fn f1(self) {}
|
||||
| ^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/self-param-semantic-fail.rs:10:11
|
||||
|
|
||||
LL | fn f2(mut self) {}
|
||||
| ^^^^^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/self-param-semantic-fail.rs:12:11
|
||||
|
|
||||
LL | fn f3(&self) {}
|
||||
| ^^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/self-param-semantic-fail.rs:14:11
|
||||
|
|
||||
LL | fn f4(&mut self) {}
|
||||
| ^^^^^^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/self-param-semantic-fail.rs:16:15
|
||||
|
|
||||
LL | fn f5<'a>(&'a self) {}
|
||||
| ^^^^^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/self-param-semantic-fail.rs:18:15
|
||||
|
|
||||
LL | fn f6<'a>(&'a mut self) {}
|
||||
| ^^^^^^^^^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/self-param-semantic-fail.rs:20:11
|
||||
|
|
||||
LL | fn f7(self: u8) {}
|
||||
| ^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/self-param-semantic-fail.rs:22:11
|
||||
|
|
||||
LL | fn f8(mut self: u8) {}
|
||||
| ^^^^^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/self-param-semantic-fail.rs:27:11
|
||||
|
|
||||
LL | fn f1(self);
|
||||
| ^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/self-param-semantic-fail.rs:29:11
|
||||
|
|
||||
LL | fn f2(mut self);
|
||||
| ^^^^^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error[E0130]: patterns aren't allowed in foreign function declarations
|
||||
--> $DIR/self-param-semantic-fail.rs:29:11
|
||||
|
|
||||
LL | fn f2(mut self);
|
||||
| ^^^^^^^^ pattern not allowed in foreign function
|
||||
|
||||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/self-param-semantic-fail.rs:32:11
|
||||
|
|
||||
LL | fn f3(&self);
|
||||
| ^^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/self-param-semantic-fail.rs:34:11
|
||||
|
|
||||
LL | fn f4(&mut self);
|
||||
| ^^^^^^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/self-param-semantic-fail.rs:36:15
|
||||
|
|
||||
LL | fn f5<'a>(&'a self);
|
||||
| ^^^^^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/self-param-semantic-fail.rs:38:15
|
||||
|
|
||||
LL | fn f6<'a>(&'a mut self);
|
||||
| ^^^^^^^^^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/self-param-semantic-fail.rs:40:11
|
||||
|
|
||||
LL | fn f7(self: u8);
|
||||
| ^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/self-param-semantic-fail.rs:42:11
|
||||
|
|
||||
LL | fn f8(mut self: u8);
|
||||
| ^^^^^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error[E0130]: patterns aren't allowed in foreign function declarations
|
||||
--> $DIR/self-param-semantic-fail.rs:42:11
|
||||
|
|
||||
LL | fn f8(mut self: u8);
|
||||
| ^^^^^^^^ pattern not allowed in foreign function
|
||||
|
||||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/self-param-semantic-fail.rs:47:14
|
||||
|
|
||||
LL | type X1 = fn(self);
|
||||
| ^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/self-param-semantic-fail.rs:49:14
|
||||
|
|
||||
LL | type X2 = fn(mut self);
|
||||
| ^^^^^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error[E0561]: patterns aren't allowed in function pointer types
|
||||
--> $DIR/self-param-semantic-fail.rs:49:14
|
||||
|
|
||||
LL | type X2 = fn(mut self);
|
||||
| ^^^^^^^^
|
||||
|
||||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/self-param-semantic-fail.rs:52:14
|
||||
|
|
||||
LL | type X3 = fn(&self);
|
||||
| ^^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/self-param-semantic-fail.rs:54:14
|
||||
|
|
||||
LL | type X4 = fn(&mut self);
|
||||
| ^^^^^^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/self-param-semantic-fail.rs:56:22
|
||||
|
|
||||
LL | type X5 = for<'a> fn(&'a self);
|
||||
| ^^^^^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/self-param-semantic-fail.rs:58:22
|
||||
|
|
||||
LL | type X6 = for<'a> fn(&'a mut self);
|
||||
| ^^^^^^^^^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/self-param-semantic-fail.rs:60:14
|
||||
|
|
||||
LL | type X7 = fn(self: u8);
|
||||
| ^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error: `self` parameter is only allowed in associated functions
|
||||
--> $DIR/self-param-semantic-fail.rs:62:14
|
||||
|
|
||||
LL | type X8 = fn(mut self: u8);
|
||||
| ^^^^^^^^ not semantically valid as function parameter
|
||||
|
|
||||
= note: associated functions are those in `impl` or `trait` definitions
|
||||
|
||||
error[E0561]: patterns aren't allowed in function pointer types
|
||||
--> $DIR/self-param-semantic-fail.rs:62:14
|
||||
|
|
||||
LL | type X8 = fn(mut self: u8);
|
||||
| ^^^^^^^^
|
||||
|
||||
error: aborting due to 28 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0130, E0561.
|
||||
For more information about an error, try `rustc --explain E0130`.
|
||||
66
src/test/ui/parser/self-param-syntactic-pass.rs
Normal file
66
src/test/ui/parser/self-param-syntactic-pass.rs
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
// This test ensures that `self` is syntactically accepted in all places an `FnDecl` is parsed.
|
||||
// FIXME(Centril): For now closures are an exception.
|
||||
|
||||
// check-pass
|
||||
|
||||
fn main() {}
|
||||
|
||||
#[cfg(FALSE)]
|
||||
fn free() {
|
||||
fn f(self) {}
|
||||
fn f(mut self) {}
|
||||
fn f(&self) {}
|
||||
fn f(&mut self) {}
|
||||
fn f(&'a self) {}
|
||||
fn f(&'a mut self) {}
|
||||
fn f(self: u8) {}
|
||||
fn f(mut self: u8) {}
|
||||
}
|
||||
|
||||
#[cfg(FALSE)]
|
||||
extern {
|
||||
fn f(self);
|
||||
fn f(mut self);
|
||||
fn f(&self);
|
||||
fn f(&mut self);
|
||||
fn f(&'a self);
|
||||
fn f(&'a mut self);
|
||||
fn f(self: u8);
|
||||
fn f(mut self: u8);
|
||||
}
|
||||
|
||||
#[cfg(FALSE)]
|
||||
trait X {
|
||||
fn f(self) {}
|
||||
fn f(mut self) {}
|
||||
fn f(&self) {}
|
||||
fn f(&mut self) {}
|
||||
fn f(&'a self) {}
|
||||
fn f(&'a mut self) {}
|
||||
fn f(self: u8) {}
|
||||
fn f(mut self: u8) {}
|
||||
}
|
||||
|
||||
#[cfg(FALSE)]
|
||||
impl X for Y {
|
||||
fn f(self) {}
|
||||
fn f(mut self) {}
|
||||
fn f(&self) {}
|
||||
fn f(&mut self) {}
|
||||
fn f(&'a self) {}
|
||||
fn f(&'a mut self) {}
|
||||
fn f(self: u8) {}
|
||||
fn f(mut self: u8) {}
|
||||
}
|
||||
|
||||
#[cfg(FALSE)]
|
||||
impl X for Y {
|
||||
type X = fn(self);
|
||||
type X = fn(mut self);
|
||||
type X = fn(&self);
|
||||
type X = fn(&mut self);
|
||||
type X = fn(&'a self);
|
||||
type X = fn(&'a mut self);
|
||||
type X = fn(self: u8);
|
||||
type X = fn(mut self: u8);
|
||||
}
|
||||
|
|
@ -5,6 +5,10 @@ LL | fn foo(Option<i32>, String) {}
|
|||
| ^ expected one of `:`, `@`, or `|`
|
||||
|
|
||||
= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
|
||||
help: if this is a `self` type, give it a parameter name
|
||||
|
|
||||
LL | fn foo(self: Option<i32>, String) {}
|
||||
| ^^^^^^^^^^^^
|
||||
help: if this is a type, explicitly ignore the parameter name
|
||||
|
|
||||
LL | fn foo(_: Option<i32>, String) {}
|
||||
|
|
@ -33,6 +37,10 @@ LL | fn bar(x, y: usize) {}
|
|||
| ^ expected one of `:`, `@`, or `|`
|
||||
|
|
||||
= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
|
||||
help: if this is a `self` type, give it a parameter name
|
||||
|
|
||||
LL | fn bar(self: x, y: usize) {}
|
||||
| ^^^^^^^
|
||||
help: if this was a parameter name, give it a type
|
||||
|
|
||||
LL | fn bar(x: TypeName, y: usize) {}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ LL | pub fn foo(Box<Self>) { }
|
|||
| ^ expected one of `:`, `@`, or `|`
|
||||
|
|
||||
= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
|
||||
help: if this is a `self` type, give it a parameter name
|
||||
|
|
||||
LL | pub fn foo(self: Box<Self>) { }
|
||||
| ^^^^^^^^^
|
||||
help: if this is a type, explicitly ignore the parameter name
|
||||
|
|
||||
LL | pub fn foo(_: Box<Self>) { }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue