Files
Luke SandbergandGitHub e0b2cebde5 Improve the NFT error message and ignore comment handling (#95144)
Fixes #95125 in two ways

The OP admits that they misread the message in a few ways, but we can
fix all of them

1. Be specific about what function might need an annotation in the error
message. A user with a nearby `path.join` might think that that is where
the annotation belongs instead of on the function with the warning.

2. Be more flexible with how these `ignore` comments are interpreted
`fs.readFileSync(path.join(process.cwd(), someVar))` shouldn't require 2
annotations. Handle this by checking for and detecting `turbopackIgnore`
comments in all expressions! This will then bubble up through the linker
and just cause us to ignore corresponding fs access apis.
2026-07-14 08:13:12 +02:00

184 lines
6.1 KiB
Rust

#![feature(arbitrary_self_types)]
#![feature(arbitrary_self_types_pointers)]
#![allow(clippy::needless_return)] // tokio macro-generated code doesn't respect this
use anyhow::Result;
use turbo_tasks::{ReadRef, ResolvedVc, TraitRef, Vc};
use turbo_tasks_testing::{Registration, register, run_once};
static REGISTRATION: Registration = register!();
#[turbo_tasks::value]
struct Wrapper(u32);
#[turbo_tasks::function]
fn returns_int(value: u32) -> Vc<u32> {
Vc::cell(value)
}
#[turbo_tasks::function]
fn assert_resolved(input: ResolvedVc<u32>) {
// double-check that this `ResolvedVc` is *actually* resolved
let input_vc: Vc<u32> = *input;
assert!(input_vc.is_resolved());
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_conversion() -> Result<()> {
// pass a nonce to re-run the test body on every turbo-tasks restart
let mut nonce = 0;
run_once(&REGISTRATION, move || {
nonce += 1;
async move {
#[turbo_tasks::function(operation, root)]
async fn test_operation(nonce: u32) -> Result<Vc<()>> {
let _ = nonce; // ensure the nonce is part of our cache key
let unresolved: Vc<u32> = Vc::cell(42);
let resolved: ResolvedVc<u32> = unresolved.to_resolved().await?;
let _: Vc<u32> = *resolved;
let _: ReadRef<u32> = resolved.await?;
let _: ReadRef<u32> = (&resolved).await?;
let _: u32 = *resolved.await?;
let _: u32 = *(&resolved).await?;
Ok(Vc::cell(()))
}
test_operation(nonce).read_strongly_consistent().await
}
})
.await
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_cell_construction() -> Result<()> {
let mut nonce = 0;
run_once(&REGISTRATION, move || {
nonce += 1;
async move {
#[turbo_tasks::function(operation, root)]
async fn test_operation(nonce: u32) -> Result<Vc<()>> {
let _ = nonce;
let a: ResolvedVc<u32> = ResolvedVc::cell(42);
assert_eq!(*a.await?, 42);
let b: ResolvedVc<Wrapper> = Wrapper(42).resolved_cell();
assert_eq!(b.await?.0, 42);
Ok(Vc::cell(()))
}
test_operation(nonce).read_strongly_consistent().await
}
})
.await
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_resolved_vc_as_arg() -> Result<()> {
let mut nonce = 0;
run_once(&REGISTRATION, move || {
nonce += 1;
async move {
#[turbo_tasks::function(operation, root)]
async fn test_operation(nonce: u32) -> Result<Vc<()>> {
dbg!(nonce);
let _ = nonce;
let unresolved: Vc<u32> = returns_int(42);
assert!(!unresolved.is_resolved());
// calling a function should cause it's arguments to get resolved automatically
assert_resolved(unresolved).await?;
Ok(Vc::cell(()))
}
test_operation(nonce).read_strongly_consistent().await
}
})
.await
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_into_future() -> Result<()> {
let mut nonce = 0;
run_once(&REGISTRATION, move || {
nonce += 1;
async move {
#[turbo_tasks::function(operation, root)]
async fn test_operation(nonce: u32) -> Result<Vc<()>> {
let _ = nonce;
let mut resolved = ResolvedVc::cell(42);
let _: ReadRef<u32> = resolved.await?;
let _: ReadRef<u32> = (&resolved).await?;
let _: ReadRef<u32> = (&mut resolved).await?;
let mut unresolved = Vc::cell(42);
let _: ReadRef<u32> = unresolved.await?;
let _: ReadRef<u32> = (&unresolved).await?;
let _: ReadRef<u32> = (&mut unresolved).await?;
Ok(Vc::cell(()))
}
test_operation(nonce).read_strongly_consistent().await
}
})
.await
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_sidecast() -> Result<()> {
run_once(&REGISTRATION, || async {
let concrete_value = ImplementsAAndB.resolved_cell();
let as_a = ResolvedVc::upcast::<Box<dyn TraitA>>(concrete_value);
let as_b = ResolvedVc::try_sidecast::<Box<dyn TraitB>>(as_a);
assert!(as_b.is_some());
let as_c = ResolvedVc::try_sidecast::<Box<dyn TraitC>>(as_a);
assert!(as_c.is_none());
Ok(())
})
.await
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_trait_ref_downcast() -> Result<()> {
run_once(&REGISTRATION, || async {
let as_base: Vc<Box<dyn BaseTrait>> = Vc::upcast(ImplementsSubImplemented.cell());
let ref_base = as_base.into_trait_ref().await?;
// The concrete value implements SubImplemented, so downcasting the already-read
// trait ref to it succeeds without another cell read.
assert!(TraitRef::try_downcast::<Box<dyn SubImplemented>>(ref_base.clone()).is_some());
// It does not implement SubNotImplemented, so the downcast returns None.
assert!(TraitRef::try_downcast::<Box<dyn SubNotImplemented>>(ref_base).is_none());
Ok(())
})
.await
}
#[turbo_tasks::value_trait]
trait TraitA {}
#[turbo_tasks::value_trait]
trait TraitB {}
#[turbo_tasks::value_trait]
trait TraitC {}
#[turbo_tasks::value]
struct ImplementsAAndB;
#[turbo_tasks::value_impl]
impl TraitA for ImplementsAAndB {}
#[turbo_tasks::value_impl]
impl TraitB for ImplementsAAndB {}
#[turbo_tasks::value_trait]
trait BaseTrait {}
// Two sub-traits of `BaseTrait`; the test's value implements only the first.
#[turbo_tasks::value_trait]
trait SubImplemented: BaseTrait {}
#[turbo_tasks::value_trait]
trait SubNotImplemented: BaseTrait {}
#[turbo_tasks::value]
struct ImplementsSubImplemented;
#[turbo_tasks::value_impl]
impl BaseTrait for ImplementsSubImplemented {}
#[turbo_tasks::value_impl]
impl SubImplemented for ImplementsSubImplemented {}