mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
refactor(traverse): speed up tests (#4538)
Closes #4537. `oxc_traverse`'s tests to ensure code which would be undefined behavior will not compile were using `trybuild`. This is a thorough way to run these tests, but requires running a lengthy compilation each time tests run. Implement these tests as `compile_fail` doc tests instead. This is not quite as thorough - now only testing that they don't compile, rather than also *why* they don't compile - but acceptable given the outsized cost of doing it "properly".
This commit is contained in:
parent
d6974d4ff7
commit
e6a8af6112
12 changed files with 86 additions and 178 deletions
76
Cargo.lock
generated
76
Cargo.lock
generated
|
|
@ -1847,7 +1847,6 @@ dependencies = [
|
||||||
"oxc_semantic",
|
"oxc_semantic",
|
||||||
"oxc_span",
|
"oxc_span",
|
||||||
"oxc_syntax",
|
"oxc_syntax",
|
||||||
"trybuild",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2449,15 +2448,6 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_spanned"
|
|
||||||
version = "0.6.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha2"
|
name = "sha2"
|
||||||
version = "0.10.8"
|
version = "0.10.8"
|
||||||
|
|
@ -2586,15 +2576,6 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "termcolor"
|
|
||||||
version = "1.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
|
|
||||||
dependencies = [
|
|
||||||
"winapi-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "textwrap"
|
name = "textwrap"
|
||||||
version = "0.16.1"
|
version = "0.16.1"
|
||||||
|
|
@ -2713,40 +2694,6 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "toml"
|
|
||||||
version = "0.8.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
"serde_spanned",
|
|
||||||
"toml_datetime",
|
|
||||||
"toml_edit",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "toml_datetime"
|
|
||||||
version = "0.6.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "toml_edit"
|
|
||||||
version = "0.22.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38"
|
|
||||||
dependencies = [
|
|
||||||
"indexmap",
|
|
||||||
"serde",
|
|
||||||
"serde_spanned",
|
|
||||||
"toml_datetime",
|
|
||||||
"winnow",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower"
|
name = "tower"
|
||||||
version = "0.4.13"
|
version = "0.4.13"
|
||||||
|
|
@ -2864,20 +2811,6 @@ dependencies = [
|
||||||
"tracing-log",
|
"tracing-log",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "trybuild"
|
|
||||||
version = "1.0.98"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b55265878356bdd85c9baa15859c87de93b2bf1f33acf752040a561e4a228f62"
|
|
||||||
dependencies = [
|
|
||||||
"glob",
|
|
||||||
"serde",
|
|
||||||
"serde_derive",
|
|
||||||
"serde_json",
|
|
||||||
"termcolor",
|
|
||||||
"toml",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tsify"
|
name = "tsify"
|
||||||
version = "0.4.5"
|
version = "0.4.5"
|
||||||
|
|
@ -3296,15 +3229,6 @@ version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winnow"
|
|
||||||
version = "0.6.13"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yansi"
|
name = "yansi"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
|
|
||||||
|
|
@ -178,7 +178,6 @@ textwrap = "0.16.1"
|
||||||
tokio = "1.38.0"
|
tokio = "1.38.0"
|
||||||
tower-lsp = "0.20.0"
|
tower-lsp = "0.20.0"
|
||||||
tracing-subscriber = "0.3.18"
|
tracing-subscriber = "0.3.18"
|
||||||
trybuild = "1.0.96"
|
|
||||||
tsify = "0.4.5"
|
tsify = "0.4.5"
|
||||||
unicode-id-start = "1" # Relaxed version so the user can decide which unicode version to use.
|
unicode-id-start = "1" # Relaxed version so the user can decide which unicode version to use.
|
||||||
unicode-width = "0.1.13"
|
unicode-width = "0.1.13"
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ workspace = true
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
test = false
|
test = false
|
||||||
|
# Doctests must be enabled for this crate as they are used to run tests
|
||||||
|
# which check the soundness of its code
|
||||||
doctest = true
|
doctest = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
@ -29,6 +31,3 @@ oxc_syntax = { workspace = true }
|
||||||
|
|
||||||
compact_str = { workspace = true }
|
compact_str = { workspace = true }
|
||||||
memoffset = { workspace = true }
|
memoffset = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
trybuild = { workspace = true }
|
|
||||||
|
|
|
||||||
81
crates/oxc_traverse/src/compile_fail_tests.rs
Normal file
81
crates/oxc_traverse/src/compile_fail_tests.rs
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
#![cfg(doctest)]
|
||||||
|
|
||||||
|
//! Tests to ensure lifetimes prevent references to escape visitor functions.
|
||||||
|
//! If they could, it'd allow aliasing, which would be undefined behavior.
|
||||||
|
//!
|
||||||
|
//! These tests were originally implemented using `trybuild` crate,
|
||||||
|
//! but it disproportionately hurt time taken to run tests.
|
||||||
|
//! So using `compile_fail` doc tests instead.
|
||||||
|
//! <https://github.com/oxc-project/oxc/issues/4537>
|
||||||
|
|
||||||
|
/**
|
||||||
|
```compile_fail
|
||||||
|
use oxc_ast::ast::IdentifierReference;
|
||||||
|
use oxc_traverse::{Ancestor, Traverse, TraverseCtx};
|
||||||
|
|
||||||
|
struct Trans<'a, 'b> {
|
||||||
|
ancestor: Option<&'b Ancestor<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> Traverse<'a> for Trans<'a, 'b> {
|
||||||
|
fn enter_identifier_reference(
|
||||||
|
&mut self,
|
||||||
|
_node: &mut IdentifierReference<'a>,
|
||||||
|
ctx: &mut TraverseCtx<'a>,
|
||||||
|
) {
|
||||||
|
self.ancestor = Some(ctx.parent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
const CANNOT_HOLD_ONTO_ANCESTOR: () = ();
|
||||||
|
|
||||||
|
/**
|
||||||
|
```compile_fail
|
||||||
|
use oxc_ast::ast::IdentifierReference;
|
||||||
|
use oxc_traverse::{ancestor::ProgramWithoutDirectives, Ancestor, Traverse, TraverseCtx};
|
||||||
|
|
||||||
|
struct Trans<'a, 'b> {
|
||||||
|
program: Option<&'b ProgramWithoutDirectives<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> Traverse<'a> for Trans<'a, 'b> {
|
||||||
|
fn enter_identifier_reference(
|
||||||
|
&mut self,
|
||||||
|
_node: &mut IdentifierReference<'a>,
|
||||||
|
ctx: &mut TraverseCtx<'a>,
|
||||||
|
) {
|
||||||
|
if let Ancestor::ProgramDirectives(program) = ctx.parent() {
|
||||||
|
self.program = Some(program);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
const CANNOT_HOLD_ONTO_ANCESTOR_NODE: () = ();
|
||||||
|
|
||||||
|
/**
|
||||||
|
```compile_fail
|
||||||
|
use oxc_ast::ast::{IdentifierReference, Statement};
|
||||||
|
use oxc_traverse::{Ancestor, Traverse, TraverseCtx};
|
||||||
|
|
||||||
|
struct Trans<'a, 'b> {
|
||||||
|
stmt: Option<&'b Statement<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> Traverse<'a> for Trans<'a, 'b> {
|
||||||
|
fn enter_identifier_reference(
|
||||||
|
&mut self,
|
||||||
|
_node: &mut IdentifierReference<'a>,
|
||||||
|
ctx: &mut TraverseCtx<'a>,
|
||||||
|
) {
|
||||||
|
if let Ancestor::ProgramDirectives(program) = ctx.parent() {
|
||||||
|
let body = program.body();
|
||||||
|
let stmt = &body[0];
|
||||||
|
self.stmt = Some(stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
const CANNOT_HOLD_ONTO_AST_NODE: () = ();
|
||||||
|
|
@ -73,6 +73,8 @@ mod traverse;
|
||||||
pub use traverse::Traverse;
|
pub use traverse::Traverse;
|
||||||
mod walk;
|
mod walk;
|
||||||
|
|
||||||
|
mod compile_fail_tests;
|
||||||
|
|
||||||
/// Traverse AST with a [`Traverse`] impl.
|
/// Traverse AST with a [`Traverse`] impl.
|
||||||
///
|
///
|
||||||
/// This allows:
|
/// This allows:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
#[test]
|
|
||||||
fn compile_fail() {
|
|
||||||
let t = trybuild::TestCases::new();
|
|
||||||
t.compile_fail("tests/compile_fail/*.rs");
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
use oxc_ast::ast::IdentifierReference;
|
|
||||||
use oxc_traverse::{Ancestor, Traverse, TraverseCtx};
|
|
||||||
|
|
||||||
struct Trans<'a, 'b> {
|
|
||||||
ancestor: Option<&'b Ancestor<'a>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b> Traverse<'a> for Trans<'a, 'b> {
|
|
||||||
fn enter_identifier_reference(
|
|
||||||
&mut self,
|
|
||||||
_node: &mut IdentifierReference<'a>,
|
|
||||||
ctx: &mut TraverseCtx<'a>,
|
|
||||||
) {
|
|
||||||
self.ancestor = Some(ctx.parent());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
error: lifetime may not live long enough
|
|
||||||
--> tests/compile_fail/ancestor_lifetime1.rs:14:9
|
|
||||||
|
|
|
||||||
8 | impl<'a, 'b> Traverse<'a> for Trans<'a, 'b> {
|
|
||||||
| -- lifetime `'b` defined here
|
|
||||||
...
|
|
||||||
12 | ctx: &mut TraverseCtx<'a>,
|
|
||||||
| - let's call the lifetime of this reference `'1`
|
|
||||||
13 | ) {
|
|
||||||
14 | self.ancestor = Some(ctx.parent());
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'b`
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
use oxc_ast::ast::IdentifierReference;
|
|
||||||
use oxc_traverse::{ancestor::ProgramWithoutDirectives, Ancestor, Traverse, TraverseCtx};
|
|
||||||
|
|
||||||
struct Trans<'a, 'b> {
|
|
||||||
program: Option<&'b ProgramWithoutDirectives<'a>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b> Traverse<'a> for Trans<'a, 'b> {
|
|
||||||
fn enter_identifier_reference(
|
|
||||||
&mut self,
|
|
||||||
_node: &mut IdentifierReference<'a>,
|
|
||||||
ctx: &mut TraverseCtx<'a>,
|
|
||||||
) {
|
|
||||||
if let Ancestor::ProgramDirectives(program) = ctx.parent() {
|
|
||||||
self.program = Some(program);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
error: lifetime may not live long enough
|
|
||||||
--> tests/compile_fail/ancestor_lifetime2.rs:15:13
|
|
||||||
|
|
|
||||||
8 | impl<'a, 'b> Traverse<'a> for Trans<'a, 'b> {
|
|
||||||
| -- lifetime `'b` defined here
|
|
||||||
...
|
|
||||||
12 | ctx: &mut TraverseCtx<'a>,
|
|
||||||
| - let's call the lifetime of this reference `'1`
|
|
||||||
...
|
|
||||||
15 | self.program = Some(program);
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'b`
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
use oxc_allocator::Vec;
|
|
||||||
use oxc_ast::ast::{IdentifierReference, Statement};
|
|
||||||
use oxc_traverse::{Ancestor, Traverse, TraverseCtx};
|
|
||||||
|
|
||||||
struct Trans<'a, 'b> {
|
|
||||||
program_body: Option<&'b Vec<'a, Statement<'a>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b> Traverse<'a> for Trans<'a, 'b> {
|
|
||||||
fn enter_identifier_reference(
|
|
||||||
&mut self,
|
|
||||||
_node: &mut IdentifierReference<'a>,
|
|
||||||
ctx: &mut TraverseCtx<'a>,
|
|
||||||
) {
|
|
||||||
if let Ancestor::ProgramDirectives(program) = ctx.parent() {
|
|
||||||
self.program_body = Some(program.body());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
error: lifetime may not live long enough
|
|
||||||
--> tests/compile_fail/ancestor_lifetime3.rs:16:13
|
|
||||||
|
|
|
||||||
9 | impl<'a, 'b> Traverse<'a> for Trans<'a, 'b> {
|
|
||||||
| -- lifetime `'b` defined here
|
|
||||||
...
|
|
||||||
13 | ctx: &mut TraverseCtx<'a>,
|
|
||||||
| - let's call the lifetime of this reference `'1`
|
|
||||||
...
|
|
||||||
16 | self.program_body = Some(program.body());
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'b`
|
|
||||||
Loading…
Reference in a new issue