mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-22 20:03:37 +00:00
add tests for macro trailing commas
This commit is contained in:
parent
ca7d839088
commit
5fa97c35da
101
src/test/compile-fail/macro-comma-behavior.rs
Normal file
101
src/test/compile-fail/macro-comma-behavior.rs
Normal file
@ -0,0 +1,101 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Companion test to the similarly-named file in run-pass.
|
||||
|
||||
// compile-flags: -C debug_assertions=yes
|
||||
// revisions: std core
|
||||
|
||||
#![cfg_attr(core, no_std)]
|
||||
|
||||
#[cfg(std)] use std::fmt;
|
||||
#[cfg(core)] use core::fmt;
|
||||
|
||||
// (see documentation of the similarly-named test in run-pass)
|
||||
fn to_format_or_not_to_format() {
|
||||
let falsum = || false;
|
||||
|
||||
// assert!(true, "{}",); // see run-pass
|
||||
|
||||
assert_eq!(1, 1, "{}",);
|
||||
//[core]~^ ERROR no arguments
|
||||
//[std]~^^ ERROR no arguments
|
||||
assert_ne!(1, 2, "{}",);
|
||||
//[core]~^ ERROR no arguments
|
||||
//[std]~^^ ERROR no arguments
|
||||
|
||||
// debug_assert!(true, "{}",); // see run-pass
|
||||
|
||||
debug_assert_eq!(1, 1, "{}",);
|
||||
//[core]~^ ERROR no arguments
|
||||
//[std]~^^ ERROR no arguments
|
||||
debug_assert_ne!(1, 2, "{}",);
|
||||
//[core]~^ ERROR no arguments
|
||||
//[std]~^^ ERROR no arguments
|
||||
|
||||
#[cfg(std)] {
|
||||
eprint!("{}",);
|
||||
//[std]~^ ERROR no arguments
|
||||
}
|
||||
|
||||
#[cfg(std)] {
|
||||
// FIXME: compile-fail says "expected error not found" even though
|
||||
// rustc does emit an error
|
||||
// eprintln!("{}",);
|
||||
// <DISABLED> [std]~^ ERROR no arguments
|
||||
}
|
||||
|
||||
#[cfg(std)] {
|
||||
format!("{}",);
|
||||
//[std]~^ ERROR no arguments
|
||||
}
|
||||
|
||||
format_args!("{}",);
|
||||
//[core]~^ ERROR no arguments
|
||||
//[std]~^^ ERROR no arguments
|
||||
|
||||
// if falsum() { panic!("{}",); } // see run-pass
|
||||
|
||||
#[cfg(std)] {
|
||||
print!("{}",);
|
||||
//[std]~^ ERROR no arguments
|
||||
}
|
||||
|
||||
#[cfg(std)] {
|
||||
// FIXME: compile-fail says "expected error not found" even though
|
||||
// rustc does emit an error
|
||||
// println!("{}",);
|
||||
// <DISABLED> [std]~^ ERROR no arguments
|
||||
}
|
||||
|
||||
unimplemented!("{}",);
|
||||
//[core]~^ ERROR no arguments
|
||||
//[std]~^^ ERROR no arguments
|
||||
|
||||
// if falsum() { unreachable!("{}",); } // see run-pass
|
||||
|
||||
struct S;
|
||||
impl fmt::Display for S {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}",)?;
|
||||
//[core]~^ ERROR no arguments
|
||||
//[std]~^^ ERROR no arguments
|
||||
|
||||
// FIXME: compile-fail says "expected error not found" even though
|
||||
// rustc does emit an error
|
||||
// writeln!(f, "{}",)?;
|
||||
// <DISABLED> [core]~^ ERROR no arguments
|
||||
// <DISABLED> [std]~^^ ERROR no arguments
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
20
src/test/compile-fail/macro-comma-support.rs
Normal file
20
src/test/compile-fail/macro-comma-support.rs
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// This is a companion to the similarly-named test in run-pass.
|
||||
//
|
||||
// It tests macros that unavoidably produce compile errors.
|
||||
|
||||
fn compile_error() {
|
||||
compile_error!("lel"); //~ ERROR lel
|
||||
compile_error!("lel",); //~ ERROR lel
|
||||
}
|
||||
|
||||
fn main() {}
|
11
src/test/run-pass/auxiliary/macro-comma-support.rs
Normal file
11
src/test/run-pass/auxiliary/macro-comma-support.rs
Normal file
@ -0,0 +1,11 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
()
|
98
src/test/run-pass/macro-comma-behavior.rs
Normal file
98
src/test/run-pass/macro-comma-behavior.rs
Normal file
@ -0,0 +1,98 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Ideally, any macro call with a trailing comma should behave
|
||||
// identically to a call without the comma.
|
||||
//
|
||||
// This checks the behavior of macros with trailing commas in key
|
||||
// places where regressions in behavior seem highly possible (due
|
||||
// to it being e.g. a place where the addition of an argument
|
||||
// causes it to go down a code path with subtly different behavior).
|
||||
//
|
||||
// There is a companion test in compile-fail.
|
||||
|
||||
// compile-flags: --test -C debug_assertions=yes
|
||||
// revisions: std core
|
||||
|
||||
#![cfg_attr(core, no_std)]
|
||||
|
||||
#[cfg(std)] use std::fmt;
|
||||
#[cfg(core)] use core::fmt;
|
||||
|
||||
// an easy mistake in the implementation of 'assert!'
|
||||
// would cause this to say "explicit panic"
|
||||
#[test]
|
||||
#[should_panic(expected = "assertion failed")]
|
||||
fn assert_1arg() {
|
||||
assert!(false,);
|
||||
}
|
||||
|
||||
// same as 'assert_1arg'
|
||||
#[test]
|
||||
#[should_panic(expected = "assertion failed")]
|
||||
fn debug_assert_1arg() {
|
||||
debug_assert!(false,);
|
||||
}
|
||||
|
||||
// make sure we don't accidentally forward to `write!("text")`
|
||||
#[cfg(std)]
|
||||
#[test]
|
||||
fn writeln_2arg() {
|
||||
use fmt::Write;
|
||||
|
||||
let mut s = String::new();
|
||||
writeln!(&mut s, "hi",).unwrap();
|
||||
assert_eq!(&s, "hi\n");
|
||||
}
|
||||
|
||||
// A number of format_args-like macros have special-case treatment
|
||||
// for a single message string, which is not formatted.
|
||||
//
|
||||
// This test ensures that the addition of a trailing comma does not
|
||||
// suddenly cause these strings to get formatted when they otherwise
|
||||
// would not be. This is an easy mistake to make by having such a macro
|
||||
// accept ", $($tok:tt)*" instead of ", $($tok:tt)+" after its minimal
|
||||
// set of arguments.
|
||||
//
|
||||
// (Example: Issue #48042)
|
||||
#[test]
|
||||
fn to_format_or_not_to_format() {
|
||||
// ("{}" is the easiest string to test because if this gets
|
||||
// sent to format_args!, it'll simply fail to compile.
|
||||
// "{{}}" is an example of an input that could compile and
|
||||
// produce an incorrect program, but testing the panics
|
||||
// would be burdensome.)
|
||||
let falsum = || false;
|
||||
|
||||
assert!(true, "{}",);
|
||||
|
||||
// assert_eq!(1, 1, "{}",); // see compile-fail
|
||||
// assert_ne!(1, 2, "{}",); // see compile-fail
|
||||
|
||||
debug_assert!(true, "{}",);
|
||||
|
||||
// debug_assert_eq!(1, 1, "{}",); // see compile-fail
|
||||
// debug_assert_ne!(1, 2, "{}",); // see compile-fail
|
||||
// eprint!("{}",); // see compile-fail
|
||||
// eprintln!("{}",); // see compile-fail
|
||||
// format!("{}",); // see compile-fail
|
||||
// format_args!("{}",); // see compile-fail
|
||||
|
||||
if falsum() { panic!("{}",); }
|
||||
|
||||
// print!("{}",); // see compile-fail
|
||||
// println!("{}",); // see compile-fail
|
||||
// unimplemented!("{}",); // see compile-fail
|
||||
|
||||
if falsum() { unreachable!("{}",); }
|
||||
|
||||
// write!(&mut stdout, "{}",); // see compile-fail
|
||||
// writeln!(&mut stdout, "{}",); // see compile-fail
|
||||
}
|
371
src/test/run-pass/macro-comma-support.rs
Normal file
371
src/test/run-pass/macro-comma-support.rs
Normal file
@ -0,0 +1,371 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// This is a comprehensive test of invocations with and without
|
||||
// trailing commas (or other, similar optionally-trailing separators).
|
||||
// Every macro is accounted for, even those not tested in this file.
|
||||
// (There will be a note indicating why).
|
||||
//
|
||||
// The expectation is for this to be updated as new macros are added,
|
||||
// or as functionality is added to existing macros.
|
||||
//
|
||||
// (FIXME: (please discuss in PR) is the above expectation reasonable?)
|
||||
|
||||
// std and core are both tested because they may contain separate
|
||||
// implementations for some macro_rules! macros as an implementation
|
||||
// detail.
|
||||
|
||||
// compile-flags: --test -C debug_assertions=yes
|
||||
// revisions: std core
|
||||
|
||||
#![cfg_attr(core, no_std)]
|
||||
|
||||
#![feature(concat_idents)]
|
||||
|
||||
#[cfg(std)] use std::fmt;
|
||||
#[cfg(core)] use core::fmt;
|
||||
|
||||
#[test]
|
||||
fn assert() {
|
||||
assert!(true);
|
||||
assert!(true,);
|
||||
assert!(true, "hello");
|
||||
assert!(true, "hello",);
|
||||
assert!(true, "hello {}", "world");
|
||||
assert!(true, "hello {}", "world",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assert_eq() {
|
||||
assert_eq!(1, 1);
|
||||
assert_eq!(1, 1,);
|
||||
assert_eq!(1, 1, "hello");
|
||||
assert_eq!(1, 1, "hello",);
|
||||
assert_eq!(1, 1, "hello {}", "world");
|
||||
assert_eq!(1, 1, "hello {}", "world",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assert_ne() {
|
||||
assert_ne!(1, 2);
|
||||
assert_ne!(1, 2,);
|
||||
assert_ne!(1, 2, "hello");
|
||||
assert_ne!(1, 2, "hello",);
|
||||
assert_ne!(1, 2, "hello {}", "world");
|
||||
assert_ne!(1, 2, "hello {}", "world",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cfg() {
|
||||
let _ = cfg!(pants);
|
||||
let _ = cfg!(pants,);
|
||||
let _ = cfg!(pants = "pants");
|
||||
let _ = cfg!(pants = "pants",);
|
||||
let _ = cfg!(all(pants));
|
||||
let _ = cfg!(all(pants),);
|
||||
let _ = cfg!(all(pants,));
|
||||
let _ = cfg!(all(pants,),);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn column() {
|
||||
let _ = column!();
|
||||
}
|
||||
|
||||
// compile_error! is in a companion to this test in compile-fail
|
||||
|
||||
#[test]
|
||||
fn concat() {
|
||||
let _ = concat!();
|
||||
let _ = concat!("hello");
|
||||
let _ = concat!("hello",);
|
||||
let _ = concat!("hello", " world");
|
||||
let _ = concat!("hello", " world",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn concat_idents() {
|
||||
fn foo() {}
|
||||
fn foobar() {}
|
||||
|
||||
concat_idents!(foo)();
|
||||
concat_idents!(foo,)();
|
||||
concat_idents!(foo, bar)();
|
||||
concat_idents!(foo, bar,)();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn debug_assert() {
|
||||
debug_assert!(true);
|
||||
debug_assert!(true, );
|
||||
debug_assert!(true, "hello");
|
||||
debug_assert!(true, "hello",);
|
||||
debug_assert!(true, "hello {}", "world");
|
||||
debug_assert!(true, "hello {}", "world",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn debug_assert_eq() {
|
||||
debug_assert_eq!(1, 1);
|
||||
debug_assert_eq!(1, 1,);
|
||||
debug_assert_eq!(1, 1, "hello");
|
||||
debug_assert_eq!(1, 1, "hello",);
|
||||
debug_assert_eq!(1, 1, "hello {}", "world");
|
||||
debug_assert_eq!(1, 1, "hello {}", "world",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn debug_assert_ne() {
|
||||
debug_assert_ne!(1, 2);
|
||||
debug_assert_ne!(1, 2,);
|
||||
debug_assert_ne!(1, 2, "hello");
|
||||
debug_assert_ne!(1, 2, "hello",);
|
||||
debug_assert_ne!(1, 2, "hello {}", "world");
|
||||
debug_assert_ne!(1, 2, "hello {}", "world",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn env() {
|
||||
let _ = env!("PATH");
|
||||
let _ = env!("PATH",);
|
||||
let _ = env!("PATH", "not found");
|
||||
let _ = env!("PATH", "not found",);
|
||||
}
|
||||
|
||||
#[cfg(std)]
|
||||
#[test]
|
||||
fn eprint() {
|
||||
eprint!("hello");
|
||||
eprint!("hello",);
|
||||
eprint!("hello {}", "world");
|
||||
eprint!("hello {}", "world",);
|
||||
}
|
||||
|
||||
#[cfg(std)]
|
||||
#[test]
|
||||
fn eprintln() {
|
||||
eprintln!();
|
||||
eprintln!("hello");
|
||||
eprintln!("hello",);
|
||||
eprintln!("hello {}", "world");
|
||||
eprintln!("hello {}", "world",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file() {
|
||||
let _ = file!();
|
||||
}
|
||||
|
||||
#[cfg(std)]
|
||||
#[test]
|
||||
fn format() {
|
||||
let _ = format!("hello");
|
||||
let _ = format!("hello",);
|
||||
let _ = format!("hello {}", "world");
|
||||
let _ = format!("hello {}", "world",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_args() {
|
||||
let _ = format_args!("hello");
|
||||
let _ = format_args!("hello",);
|
||||
let _ = format_args!("hello {}", "world");
|
||||
let _ = format_args!("hello {}", "world",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn include() {
|
||||
let _ = include!("auxiliary/macro-comma-support.rs");
|
||||
let _ = include!("auxiliary/macro-comma-support.rs",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn include_bytes() {
|
||||
let _ = include_bytes!("auxiliary/macro-comma-support.rs");
|
||||
let _ = include_bytes!("auxiliary/macro-comma-support.rs",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn include_str() {
|
||||
let _ = include_str!("auxiliary/macro-comma-support.rs");
|
||||
let _ = include_str!("auxiliary/macro-comma-support.rs",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn line() {
|
||||
let _ = line!();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn module_path() {
|
||||
let _ = module_path!();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn option_env() {
|
||||
let _ = option_env!("PATH");
|
||||
let _ = option_env!("PATH",);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn panic() {
|
||||
// prevent 'unreachable code' warnings
|
||||
let falsum = || false;
|
||||
|
||||
if falsum() { panic!(); }
|
||||
if falsum() { panic!("hello"); }
|
||||
if falsum() { panic!("hello",); }
|
||||
if falsum() { panic!("hello {}", "world"); }
|
||||
if falsum() { panic!("hello {}", "world",); }
|
||||
}
|
||||
|
||||
#[cfg(std)]
|
||||
#[test]
|
||||
fn print() {
|
||||
print!("hello");
|
||||
print!("hello",);
|
||||
print!("hello {}", "world");
|
||||
print!("hello {}", "world",);
|
||||
}
|
||||
|
||||
#[cfg(std)]
|
||||
#[test]
|
||||
fn println() {
|
||||
println!();
|
||||
println!("hello");
|
||||
println!("hello",);
|
||||
println!("hello {}", "world");
|
||||
println!("hello {}", "world",);
|
||||
}
|
||||
|
||||
// FIXME: select! (please discuss in PR)
|
||||
//
|
||||
// Test cases for select! are obnoxiously large, see here:
|
||||
//
|
||||
// https://github.com/ExpHP/rust-macro-comma-test/blob/0062e75e01ab/src/main.rs#L190-L250
|
||||
//
|
||||
// and due to other usability issues described there, it is unclear to me that it is
|
||||
// going anywhere in its current state. This is a job far too big for a macro_rules! macro,
|
||||
// and for as long as it exists in this form it will have many many problems far worse than
|
||||
// just lack of trailing comma support.
|
||||
|
||||
// stringify! is N/A
|
||||
|
||||
#[cfg(std)]
|
||||
#[test]
|
||||
fn thread_local() {
|
||||
// this has an optional trailing *semicolon*
|
||||
thread_local! {
|
||||
#[allow(unused)] pub static A: () = ()
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
#[allow(unused)] pub static AA: () = ();
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
#[allow(unused)] pub static AAA: () = ();
|
||||
#[allow(unused)] pub static AAAA: () = ()
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
#[allow(unused)] pub static AAAAG: () = ();
|
||||
#[allow(unused)] pub static AAAAGH: () = ();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try() {
|
||||
fn inner() -> Result<(), ()> {
|
||||
try!(Ok(()));
|
||||
try!(Ok(()),);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
inner().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unimplemented() {
|
||||
// prevent 'unreachable code' warnings
|
||||
let falsum = || false;
|
||||
|
||||
if falsum() { unimplemented!(); }
|
||||
if falsum() { unimplemented!("hello"); }
|
||||
if falsum() { unimplemented!("hello",); }
|
||||
if falsum() { unimplemented!("hello {}", "world"); }
|
||||
if falsum() { unimplemented!("hello {}", "world",); }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unreachable() {
|
||||
// prevent 'unreachable code' warnings
|
||||
let falsum = || false;
|
||||
|
||||
if falsum() { unreachable!(); }
|
||||
if falsum() { unreachable!("hello"); }
|
||||
if falsum() { unreachable!("hello",); }
|
||||
if falsum() { unreachable!("hello {}", "world"); }
|
||||
if falsum() { unreachable!("hello {}", "world",); }
|
||||
}
|
||||
|
||||
#[cfg(std)]
|
||||
#[test]
|
||||
fn vec() {
|
||||
let _: Vec<()> = vec![];
|
||||
let _ = vec![0];
|
||||
let _ = vec![0,];
|
||||
let _ = vec![0, 1];
|
||||
let _ = vec![0, 1,];
|
||||
}
|
||||
|
||||
// give a test body access to a fmt::Formatter, which seems
|
||||
// to be the easiest way to use 'write!' on core.
|
||||
macro_rules! test_with_formatter {
|
||||
(
|
||||
#[test]
|
||||
fn $fname:ident($f:ident: &mut fmt::Formatter) $block:block
|
||||
) => {
|
||||
#[test]
|
||||
fn $fname() {
|
||||
struct Struct;
|
||||
impl fmt::Display for Struct {
|
||||
fn fmt(&self, $f: &mut fmt::Formatter) -> fmt::Result {
|
||||
Ok($block)
|
||||
}
|
||||
}
|
||||
|
||||
// suppress "unused"
|
||||
assert!(true, "{}", Struct);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test_with_formatter! {
|
||||
#[test]
|
||||
fn write(f: &mut fmt::Formatter) {
|
||||
let _ = write!(f, "hello");
|
||||
let _ = write!(f, "hello",);
|
||||
let _ = write!(f, "hello {}", "world");
|
||||
let _ = write!(f, "hello {}", "world",);
|
||||
}
|
||||
}
|
||||
|
||||
test_with_formatter! {
|
||||
#[test]
|
||||
fn writeln(f: &mut fmt::Formatter) {
|
||||
let _ = writeln!(f);
|
||||
let _ = writeln!(f,);
|
||||
let _ = writeln!(f, "hello");
|
||||
let _ = writeln!(f, "hello",);
|
||||
let _ = writeln!(f, "hello {}", "world");
|
||||
let _ = writeln!(f, "hello {}", "world",);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user