about summary refs log tree commit diff
path: root/tests
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-12-02 18:36:36 +0000
committerbors <bors@rust-lang.org>2024-12-02 18:36:36 +0000
commitd49be02cf6d2e2a01264fcdef1e20c826710c0f5 (patch)
tree873ce3d38ea75bfa910a38eab39e6d834c13ca17 /tests
parent32eea2f4460b06b12acc98050a4211b8c0ccfd67 (diff)
parent586591f4d6f656fc2eaf98f33f248bac2b1c3e4f (diff)
downloadrust-d49be02cf6d2e2a01264fcdef1e20c826710c0f5.tar.gz
rust-d49be02cf6d2e2a01264fcdef1e20c826710c0f5.zip
Auto merge of #133760 - GuillaumeGomez:rollup-2c1y8c3, r=GuillaumeGomez
Rollup of 13 pull requests

Successful merges:

 - #133603 (Eliminate magic numbers from expression precedence)
 - #133715 (rustdoc-json: Include safety of `static`s)
 - #133721 (rustdoc-json: Add test for `impl Trait for dyn Trait`)
 - #133725 (Remove `//@ compare-output-lines-by-subset`)
 - #133730 (Add pretty-printer parenthesis insertion test)
 - #133736 (Add `needs-target-has-atomic` directive)
 - #133739 (Re-add myself to rotation)
 - #133743 (Fix docs for `<[T]>::as_array`.)
 - #133744 (Fix typo README.md)
 - #133745 (Remove static HashSet for default IDs list)
 - #133749 (mir validator: don't store mir phase)
 - #133751 (remove `Ty::is_copy_modulo_regions`)
 - #133757 (`impl Default for EarlyDiagCtxt`)

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'tests')
-rw-r--r--tests/rustdoc-json/impls/trait-for-dyn-trait.rs15
-rw-r--r--tests/rustdoc-json/statics/extern.rs39
-rw-r--r--tests/rustdoc-json/statics/statics.rs12
-rw-r--r--tests/ui-fulldeps/pprust-parenthesis-insertion.rs242
-rw-r--r--tests/ui/README.md6
5 files changed, 311 insertions, 3 deletions
diff --git a/tests/rustdoc-json/impls/trait-for-dyn-trait.rs b/tests/rustdoc-json/impls/trait-for-dyn-trait.rs
new file mode 100644
index 00000000000..0fbb4df0028
--- /dev/null
+++ b/tests/rustdoc-json/impls/trait-for-dyn-trait.rs
@@ -0,0 +1,15 @@
+//@ set t1 = '$.index[*][?(@.name=="T1")].id'
+pub trait T1 {}
+
+//@ set t2 = '$.index[*][?(@.name=="T2")].id'
+pub trait T2 {}
+
+/// Fun impl
+impl T1 for dyn T2 {}
+
+//@ set impl = '$.index[*][?(@.docs=="Fun impl")].id'
+//@ is '$.index[*][?(@.name=="T1")].inner.trait.implementations[*]' $impl
+//@ is '$.index[*][?(@.name=="T2")].inner.trait.implementations' []
+
+//@ is '$.index[*][?(@.docs=="Fun impl")].inner.impl.trait.id' $t1
+//@ is '$.index[*][?(@.docs=="Fun impl")].inner.impl.for.dyn_trait.traits[*].trait.id' $t2
diff --git a/tests/rustdoc-json/statics/extern.rs b/tests/rustdoc-json/statics/extern.rs
new file mode 100644
index 00000000000..d38fdf1cd1c
--- /dev/null
+++ b/tests/rustdoc-json/statics/extern.rs
@@ -0,0 +1,39 @@
+// ignore-tidy-linelength
+//@ edition: 2021
+
+extern "C" {
+    //@ is '$.index[*][?(@.name=="A")].inner.static.is_unsafe'  true
+    //@ is '$.index[*][?(@.name=="A")].inner.static.is_mutable' false
+    pub static A: i32;
+    //@ is '$.index[*][?(@.name=="B")].inner.static.is_unsafe'  true
+    //@ is '$.index[*][?(@.name=="B")].inner.static.is_mutable' true
+    pub static mut B: i32;
+
+    // items in unadorned `extern` blocks cannot have safety qualifiers
+}
+
+unsafe extern "C" {
+    //@ is '$.index[*][?(@.name=="C")].inner.static.is_unsafe'  true
+    //@ is '$.index[*][?(@.name=="C")].inner.static.is_mutable' false
+    pub static C: i32;
+    //@ is '$.index[*][?(@.name=="D")].inner.static.is_unsafe'  true
+    //@ is '$.index[*][?(@.name=="D")].inner.static.is_mutable' true
+    pub static mut D: i32;
+
+    //@ is '$.index[*][?(@.name=="E")].inner.static.is_unsafe'  false
+    //@ is '$.index[*][?(@.name=="E")].inner.static.is_mutable' false
+    pub safe static E: i32;
+    //@ is '$.index[*][?(@.name=="F")].inner.static.is_unsafe'  false
+    //@ is '$.index[*][?(@.name=="F")].inner.static.is_mutable' true
+    pub safe static mut F: i32;
+
+    //@ is '$.index[*][?(@.name=="G")].inner.static.is_unsafe'  true
+    //@ is '$.index[*][?(@.name=="G")].inner.static.is_mutable' false
+    pub unsafe static G: i32;
+    //@ is '$.index[*][?(@.name=="H")].inner.static.is_unsafe'  true
+    //@ is '$.index[*][?(@.name=="H")].inner.static.is_mutable' true
+    pub unsafe static mut H: i32;
+}
+
+//@ ismany '$.index[*][?(@.inner.static)].inner.static.expr' '""' '""' '""' '""' '""' '""' '""' '""'
+//@ ismany '$.index[*][?(@.inner.static)].inner.static.type.primitive' '"i32"' '"i32"' '"i32"' '"i32"' '"i32"' '"i32"' '"i32"' '"i32"'
diff --git a/tests/rustdoc-json/statics/statics.rs b/tests/rustdoc-json/statics/statics.rs
new file mode 100644
index 00000000000..a8af23cc87d
--- /dev/null
+++ b/tests/rustdoc-json/statics/statics.rs
@@ -0,0 +1,12 @@
+//@ is '$.index[*][?(@.name=="A")].inner.static.type.primitive' '"i32"'
+//@ is '$.index[*][?(@.name=="A")].inner.static.is_mutable' false
+//@ is '$.index[*][?(@.name=="A")].inner.static.expr' '"5"'
+//@ is '$.index[*][?(@.name=="A")].inner.static.is_unsafe' false
+pub static A: i32 = 5;
+
+//@ is '$.index[*][?(@.name=="B")].inner.static.type.primitive' '"u32"'
+//@ is '$.index[*][?(@.name=="B")].inner.static.is_mutable' true
+// Expr value isn't gaurenteed, it'd be fine to change it.
+//@ is '$.index[*][?(@.name=="B")].inner.static.expr' '"_"'
+//@ is '$.index[*][?(@.name=="B")].inner.static.is_unsafe' false
+pub static mut B: u32 = 2 + 3;
diff --git a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs
new file mode 100644
index 00000000000..fd6644d73c1
--- /dev/null
+++ b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs
@@ -0,0 +1,242 @@
+//@ run-pass
+//@ ignore-cross-compile
+
+// This test covers the AST pretty-printer's automatic insertion of parentheses
+// into unparenthesized syntax trees according to precedence and various grammar
+// restrictions and edge cases.
+//
+// For example if the following syntax tree represents the expression a*(b+c),
+// in which the parenthesis is necessary for precedence:
+//
+//     Binary('*', Path("a"), Paren(Binary('+', Path("b"), Path("c"))))
+//
+// then the pretty-printer needs to be able to print the following
+// unparenthesized syntax tree with an automatically inserted parenthesization.
+//
+//     Binary('*', Path("a"), Binary('+', Path("b"), Path("c")))
+//
+// Handling this correctly is relevant in real-world code when pretty-printing
+// macro-generated syntax trees, in which expressions can get interpolated into
+// one another without any parenthesization being visible in the syntax tree.
+//
+//     macro_rules! repro {
+//         ($rhs:expr) => {
+//             a * $rhs
+//         };
+//     }
+//
+//     let _ = repro!(b + c);
+
+#![feature(rustc_private)]
+
+extern crate rustc_ast;
+extern crate rustc_ast_pretty;
+extern crate rustc_driver;
+extern crate rustc_errors;
+extern crate rustc_parse;
+extern crate rustc_session;
+extern crate rustc_span;
+extern crate smallvec;
+
+use std::mem;
+use std::process::ExitCode;
+
+use rustc_ast::ast::{DUMMY_NODE_ID, Expr, ExprKind, Stmt};
+use rustc_ast::mut_visit::{self, DummyAstNode as _, MutVisitor};
+use rustc_ast::node_id::NodeId;
+use rustc_ast::ptr::P;
+use rustc_ast_pretty::pprust;
+use rustc_errors::Diag;
+use rustc_parse::parser::Recovery;
+use rustc_session::parse::ParseSess;
+use rustc_span::{DUMMY_SP, FileName, Span};
+use smallvec::SmallVec;
+
+// Every parenthesis in the following expressions is re-inserted by the
+// pretty-printer.
+//
+// FIXME: Some of them shouldn't be.
+static EXPRS: &[&str] = &[
+    // Straightforward binary operator precedence.
+    "2 * 2 + 2",
+    "2 + 2 * 2",
+    "(2 + 2) * 2",
+    "2 * (2 + 2)",
+    "2 + 2 + 2",
+    // Return has lower precedence than a binary operator.
+    "(return 2) + 2",
+    "2 + (return 2)", // FIXME: no parenthesis needed.
+    "(return) + 2",   // FIXME: no parenthesis needed.
+    // These mean different things.
+    "return - 2",
+    "(return) - 2",
+    // These mean different things.
+    "if let _ = true && false {}",
+    "if let _ = (true && false) {}",
+    // Conditions end at the first curly brace, so struct expressions need to be
+    // parenthesized. Except in a match guard, where conditions end at arrow.
+    "if let _ = (Struct {}) {}",
+    "match 2 { _ if let _ = Struct {} => {} }",
+    // Match arms terminate eagerly, so parenthesization is needed around some
+    // expressions.
+    "match 2 { _ => 1 - 1 }",
+    "match 2 { _ => ({ 1 }) - 1 }",
+    // Grammar restriction: break value starting with a labeled loop is not
+    // allowed, except if the break is also labeled.
+    "break 'outer 'inner: loop {} + 2",
+    "break ('inner: loop {} + 2)",
+    // Grammar restriction: the value in let-else is not allowed to end in a
+    // curly brace.
+    "{ let _ = 1 + 1 else {}; }",
+    "{ let _ = (loop {}) else {}; }",
+    "{ let _ = mac!() else {}; }",
+    "{ let _ = (mac! {}) else {}; }",
+    // Parentheses are necessary to prevent an eager statement boundary.
+    "{ 2 - 1 }",
+    "{ (match 2 {}) - 1 }",
+    "{ (match 2 {})() - 1 }",
+    "{ (match 2 {})[0] - 1 }",
+    "{ (loop {}) - 1 }",
+    // Angle bracket is eagerly parsed as a path's generic argument list.
+    "(2 as T) < U",
+    "(2 as T<U>) < V", // FIXME: no parentheses needed.
+    /*
+    // FIXME: pretty-printer produces invalid syntax. `2 + 2 as T < U`
+    "(2 + 2 as T) < U",
+    */
+    /*
+    // FIXME: pretty-printer produces invalid syntax. `if (let _ = () && Struct {}.x) {}`
+    "if let _ = () && (Struct {}).x {}",
+    */
+    /*
+    // FIXME: pretty-printer produces invalid syntax. `(1 < 2 == false) as usize`
+    "((1 < 2) == false) as usize",
+    */
+    /*
+    // FIXME: pretty-printer produces invalid syntax. `for _ in 1..{ 2 } {}`
+    "for _ in (1..{ 2 }) {}",
+    */
+    /*
+    // FIXME: pretty-printer loses the attribute. `{ let Struct { field } = s; }`
+    "{ let Struct { #[attr] field } = s; }",
+    */
+    /*
+    // FIXME: pretty-printer turns this into a range. `0..to_string()`
+    "(0.).to_string()",
+    "0. .. 1.",
+    */
+    /*
+    // FIXME: pretty-printer loses the dyn*. `i as Trait`
+    "i as dyn* Trait",
+    */
+];
+
+// Flatten the content of parenthesis nodes into their parent node. For example
+// this syntax tree representing the expression a*(b+c):
+//
+//     Binary('*', Path("a"), Paren(Binary('+', Path("b"), Path("c"))))
+//
+// would unparenthesize to:
+//
+//     Binary('*', Path("a"), Binary('+', Path("b"), Path("c")))
+struct Unparenthesize;
+
+impl MutVisitor for Unparenthesize {
+    fn visit_expr(&mut self, e: &mut P<Expr>) {
+        while let ExprKind::Paren(paren) = &mut e.kind {
+            **e = mem::replace(&mut *paren, Expr::dummy());
+        }
+        mut_visit::walk_expr(self, e);
+    }
+}
+
+// Erase Span information that could distinguish between identical expressions
+// parsed from different source strings.
+struct Normalize;
+
+impl MutVisitor for Normalize {
+    const VISIT_TOKENS: bool = true;
+
+    fn visit_id(&mut self, id: &mut NodeId) {
+        *id = DUMMY_NODE_ID;
+    }
+
+    fn visit_span(&mut self, span: &mut Span) {
+        *span = DUMMY_SP;
+    }
+
+    fn visit_expr(&mut self, expr: &mut P<Expr>) {
+        if let ExprKind::Binary(binop, _left, _right) = &mut expr.kind {
+            self.visit_span(&mut binop.span);
+        }
+        mut_visit::walk_expr(self, expr);
+    }
+
+    fn flat_map_stmt(&mut self, mut stmt: Stmt) -> SmallVec<[Stmt; 1]> {
+        self.visit_span(&mut stmt.span);
+        mut_visit::walk_flat_map_stmt(self, stmt)
+    }
+}
+
+fn parse_expr(psess: &ParseSess, source_code: &str) -> Option<P<Expr>> {
+    let parser = rustc_parse::unwrap_or_emit_fatal(rustc_parse::new_parser_from_source_str(
+        psess,
+        FileName::anon_source_code(source_code),
+        source_code.to_owned(),
+    ));
+
+    let mut expr = parser.recovery(Recovery::Forbidden).parse_expr().map_err(Diag::cancel).ok()?;
+    Normalize.visit_expr(&mut expr);
+    Some(expr)
+}
+
+fn main() -> ExitCode {
+    let mut status = ExitCode::SUCCESS;
+    let mut fail = |description: &str, before: &str, after: &str| {
+        status = ExitCode::FAILURE;
+        eprint!(
+            "{description}\n  BEFORE: {before}\n   AFTER: {after}\n\n",
+            before = before.replace('\n', "\n          "),
+            after = after.replace('\n', "\n          "),
+        );
+    };
+
+    rustc_span::create_default_session_globals_then(|| {
+        let psess = &ParseSess::new(vec![rustc_parse::DEFAULT_LOCALE_RESOURCE]);
+
+        for &source_code in EXPRS {
+            let expr = parse_expr(psess, source_code).unwrap();
+
+            // Check for FALSE POSITIVE: pretty-printer inserting parentheses where not needed.
+            // Pseudocode:
+            //   assert(expr == parse(print(expr)))
+            let printed = &pprust::expr_to_string(&expr);
+            let Some(expr2) = parse_expr(psess, printed) else {
+                fail("Pretty-printer produced invalid syntax", source_code, printed);
+                continue;
+            };
+            if format!("{expr:#?}") != format!("{expr2:#?}") {
+                fail("Pretty-printer inserted unnecessary parenthesis", source_code, printed);
+                continue;
+            }
+
+            // Check for FALSE NEGATIVE: pretty-printer failing to place necessary parentheses.
+            // Pseudocode:
+            //   assert(unparenthesize(expr) == unparenthesize(parse(print(unparenthesize(expr)))))
+            let mut expr = expr;
+            Unparenthesize.visit_expr(&mut expr);
+            let printed = &pprust::expr_to_string(&expr);
+            let Some(mut expr2) = parse_expr(psess, printed) else {
+                fail("Pretty-printer with no parens produced invalid syntax", source_code, printed);
+                continue;
+            };
+            Unparenthesize.visit_expr(&mut expr2);
+            if format!("{expr:#?}") != format!("{expr2:#?}") {
+                fail("Pretty-printer lost necessary parentheses", source_code, printed);
+                continue;
+            }
+        }
+    });
+
+    status
+}
diff --git a/tests/ui/README.md b/tests/ui/README.md
index c14d0ee78c8..aa36481ae06 100644
--- a/tests/ui/README.md
+++ b/tests/ui/README.md
@@ -6,9 +6,9 @@ This folder contains `rustc`'s
 ## Test Directives (Headers)
 
 Typically, a UI test will have some test directives / headers which are
-special comments that tell compiletest how to build and intepret a test.
+special comments that tell compiletest how to build and interpret a test.
 
-As part of an on-going effort to rewrite compiletest
+As part of an ongoing effort to rewrite compiletest
 (see <https://github.com/rust-lang/compiler-team/issues/536>), a major
 change proposal to change legacy compiletest-style headers `// <directive>`
 to [`ui_test`](https://github.com/oli-obk/ui_test)-style headers
@@ -30,6 +30,6 @@ but in `ui_test` style, the header would be written as
 
 compiletest is changed to accept only `//@` directives for UI tests
 (currently), and will reject and report an error if it encounters any
-comments `// <content>` that may be parsed as an legacy compiletest-style
+comments `// <content>` that may be parsed as a legacy compiletest-style
 test header. To fix this, you should migrate to the `ui_test`-style header
 `//@ <content>`.