Unnamed repository; edit this file 'description' to name the repository.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//! Defines a unit of change that can applied to the database to get the next
//! state. Changes are transactional.

use std::fmt;

use salsa::Durability;
use triomphe::Arc;
use vfs::FileId;

use crate::{CrateGraph, SourceDatabaseExt, SourceRoot, SourceRootId};

/// Encapsulate a bunch of raw `.set` calls on the database.
#[derive(Default)]
pub struct FileChange {
    pub roots: Option<Vec<SourceRoot>>,
    pub files_changed: Vec<(FileId, Option<Arc<str>>)>,
    pub crate_graph: Option<CrateGraph>,
}

impl fmt::Debug for FileChange {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut d = fmt.debug_struct("Change");
        if let Some(roots) = &self.roots {
            d.field("roots", roots);
        }
        if !self.files_changed.is_empty() {
            d.field("files_changed", &self.files_changed.len());
        }
        if self.crate_graph.is_some() {
            d.field("crate_graph", &self.crate_graph);
        }
        d.finish()
    }
}

impl FileChange {
    pub fn new() -> Self {
        FileChange::default()
    }

    pub fn set_roots(&mut self, roots: Vec<SourceRoot>) {
        self.roots = Some(roots);
    }

    pub fn change_file(&mut self, file_id: FileId, new_text: Option<Arc<str>>) {
        self.files_changed.push((file_id, new_text))
    }

    pub fn set_crate_graph(&mut self, graph: CrateGraph) {
        self.crate_graph = Some(graph);
    }

    pub fn apply(self, db: &mut dyn SourceDatabaseExt) {
        let _p = profile::span("RootDatabase::apply_change");
        if let Some(roots) = self.roots {
            for (idx, root) in roots.into_iter().enumerate() {
                let root_id = SourceRootId(idx as u32);
                let durability = durability(&root);
                for file_id in root.iter() {
                    db.set_file_source_root_with_durability(file_id, root_id, durability);
                }
                db.set_source_root_with_durability(root_id, Arc::new(root), durability);
            }
        }

        for (file_id, text) in self.files_changed {
            let source_root_id = db.file_source_root(file_id);
            let source_root = db.source_root(source_root_id);
            let durability = durability(&source_root);
            // XXX: can't actually remove the file, just reset the text
            let text = text.unwrap_or_else(|| Arc::from(""));
            db.set_file_text_with_durability(file_id, text, durability)
        }
        if let Some(crate_graph) = self.crate_graph {
            db.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH);
        }
    }
}

fn durability(source_root: &SourceRoot) -> Durability {
    if source_root.is_library {
        Durability::HIGH
    } else {
        Durability::LOW
    }
}
#n212'>212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
use std::ops::ControlFlow;

use hir_def::db::DefDatabase;
use rustc_hash::{FxHashMap, FxHashSet};
use syntax::ToSmolStr;
use test_fixture::WithFixture;

use crate::{dyn_compatibility::dyn_compatibility_with_callback, test_db::TestDB};

use super::{
    DynCompatibilityViolation,
    MethodViolationCode::{self, *},
};

use DynCompatibilityViolationKind::*;

#[allow(clippy::upper_case_acronyms)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum DynCompatibilityViolationKind {
    SizedSelf,
    SelfReferential,
    Method(MethodViolationCode),
    AssocConst,
    GAT,
    HasNonCompatibleSuperTrait,
}

fn check_dyn_compatibility<'a>(
    #[rust_analyzer::rust_fixture] ra_fixture: &str,
    expected: impl IntoIterator<Item = (&'a str, Vec<DynCompatibilityViolationKind>)>,
) {
    let mut expected: FxHashMap<_, _> =
        expected.into_iter().map(|(id, osvs)| (id, FxHashSet::from_iter(osvs))).collect();
    let (db, file_ids) = TestDB::with_many_files(ra_fixture);
    for (trait_id, name) in file_ids.into_iter().flat_map(|file_id| {
        let module_id = db.module_for_file(file_id);
        let def_map = module_id.def_map(&db);
        let scope = &def_map[module_id.local_id].scope;
        scope
            .declarations()
            .filter_map(|def| {
                if let hir_def::ModuleDefId::TraitId(trait_id) = def {
                    let name =
                        db.trait_data(trait_id).name.display_no_db(file_id.edition()).to_smolstr();
                    Some((trait_id, name))
                } else {
                    None
                }
            })
            .collect::<Vec<_>>()
    }) {
        let Some(expected) = expected.remove(name.as_str()) else {
            continue;
        };
        let mut osvs = FxHashSet::default();
        let _ = dyn_compatibility_with_callback(&db, trait_id, &mut |osv| {
            osvs.insert(match osv {
                DynCompatibilityViolation::SizedSelf => SizedSelf,
                DynCompatibilityViolation::SelfReferential => SelfReferential,
                DynCompatibilityViolation::Method(_, mvc) => Method(mvc),
                DynCompatibilityViolation::AssocConst(_) => AssocConst,
                DynCompatibilityViolation::GAT(_) => GAT,
                DynCompatibilityViolation::HasNonCompatibleSuperTrait(_) => {
                    HasNonCompatibleSuperTrait
                }
            });
            ControlFlow::Continue(())
        });
        assert_eq!(osvs, expected, "dyn-compatibility violations for `{name}` do not match;");
    }

    let remains: Vec<_> = expected.keys().collect();
    assert!(remains.is_empty(), "Following traits do not exist in the test fixture; {remains:?}");
}

#[test]
fn item_bounds_can_reference_self() {
    check_dyn_compatibility(
        r#"
//- minicore: eq
pub trait Foo {
    type X: PartialEq;
    type Y: PartialEq<Self::Y>;
    type Z: PartialEq<Self::Y>;
}
"#,
        [("Foo", vec![])],
    );
}

#[test]
fn associated_consts() {
    check_dyn_compatibility(
        r#"
trait Bar {
    const X: usize;
}
"#,
        [("Bar", vec![AssocConst])],
    );
}

#[test]
fn bounds_reference_self() {
    check_dyn_compatibility(
        r#"
//- minicore: eq
trait X {
    type U: PartialEq<Self>;
}
"#,
        [("X", vec![SelfReferential])],
    );
}

#[test]
fn by_value_self() {
    check_dyn_compatibility(
        r#"
//- minicore: dispatch_from_dyn
trait Bar {
    fn bar(self);
}

trait Baz {
    fn baz(self: Self);
}

trait Quux {
    // Legal because of the where clause:
    fn baz(self: Self) where Self : Sized;
}
"#,
        [("Bar", vec![]), ("Baz", vec![]), ("Quux", vec![])],
    );
}

#[test]
fn generic_methods() {
    check_dyn_compatibility(
        r#"
//- minicore: dispatch_from_dyn
trait Bar {
    fn bar<T>(&self, t: T);
}

trait Quux {
    fn bar<T>(&self, t: T)
        where Self : Sized;
}

trait Qax {
    fn bar<'a>(&self, t: &'a ());
}
"#,
        [("Bar", vec![Method(Generic)]), ("Quux", vec![]), ("Qax", vec![])],
    );
}

#[test]
fn mentions_self() {
    check_dyn_compatibility(
        r#"
//- minicore: dispatch_from_dyn
trait Bar {
    fn bar(&self, x: &Self);
}

trait Baz {
    fn baz(&self) -> Self;
}

trait Quux {
    fn quux(&self, s: &Self) -> Self where Self : Sized;
}
"#,
        [
            ("Bar", vec![Method(ReferencesSelfInput)]),
            ("Baz", vec![Method(ReferencesSelfOutput)]),
            ("Quux", vec![]),
        ],
    );
}

#[test]
fn no_static() {
    check_dyn_compatibility(
        r#"
//- minicore: dispatch_from_dyn
trait Foo {
    fn foo() {}
}
"#,
        [("Foo", vec![Method(StaticMethod)])],
    );
}

#[test]
fn sized_self() {
    check_dyn_compatibility(
        r#"
//- minicore: dispatch_from_dyn
trait Bar: Sized {
    fn bar<T>(&self, t: T);
}
"#,
        [("Bar", vec![SizedSelf])],
    );

    check_dyn_compatibility(
        r#"
//- minicore: dispatch_from_dyn
trait Bar
    where Self : Sized
{
    fn bar<T>(&self, t: T);
}
"#,
        [("Bar", vec![SizedSelf])],
    );
}

#[test]
fn supertrait_gat() {
    check_dyn_compatibility(
        r#"
//- minicore: dispatch_from_dyn
trait GatTrait {
    type Gat<T>;
}

trait SuperTrait<T>: GatTrait {}
"#,
        [("GatTrait", vec![GAT]), ("SuperTrait", vec![HasNonCompatibleSuperTrait])],
    );
}

#[test]
fn supertrait_mentions_self() {
    check_dyn_compatibility(
        r#"
//- minicore: dispatch_from_dyn
trait Bar<T> {
    fn bar(&self, x: &T);
}

trait Baz : Bar<Self> {
}
"#,
        [("Bar", vec![]), ("Baz", vec![SizedSelf, SelfReferential])],
    );
}

#[test]
fn rustc_issue_19538() {
    check_dyn_compatibility(
        r#"
//- minicore: dispatch_from_dyn
trait Foo {
    fn foo<T>(&self, val: T);
}

trait Bar: Foo {}
"#,
        [("Foo", vec![Method(Generic)]), ("Bar", vec![HasNonCompatibleSuperTrait])],
    );
}

#[test]
fn rustc_issue_22040() {
    check_dyn_compatibility(
        r#"
//- minicore: fmt, eq, dispatch_from_dyn
use core::fmt::Debug;

trait Expr: Debug + PartialEq {
    fn print_element_count(&self);
}
"#,
        [("Expr", vec![SelfReferential])],
    );
}

#[test]
fn rustc_issue_102762() {
    check_dyn_compatibility(
        r#"
//- minicore: future, send, sync, dispatch_from_dyn, deref
use core::pin::Pin;

struct Box<T: ?Sized> {}
impl<T: ?Sized> core::ops::Deref for Box<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        loop {}
    }
}
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Box<U>> for Box<T> {}

struct Vec<T> {}

pub trait Fetcher: Send + Sync {
    fn get<'a>(self: &'a Box<Self>) -> Pin<Box<dyn Future<Output = Vec<u8>> + 'a>>
    where
        Self: Sync,
    {
        loop {}
    }
}
"#,
        [("Fetcher", vec![Method(UndispatchableReceiver)])],
    );
}

#[test]
fn rustc_issue_102933() {
    check_dyn_compatibility(
        r#"
//- minicore: future, dispatch_from_dyn, deref
use core::future::Future;

struct Box<T: ?Sized> {}
impl<T: ?Sized> core::ops::Deref for Box<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        loop {}
    }
}
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Box<U>> for Box<T> {}

pub trait Service {
    type Response;
    type Future: Future<Output = Self::Response>;
}

pub trait A1: Service<Response = i32> {}

pub trait A2: Service<Future = Box<dyn Future<Output = i32>>> + A1 {
    fn foo(&self) {}
}

pub trait B1: Service<Future = Box<dyn Future<Output = i32>>> {}

pub trait B2: Service<Response = i32> + B1 {
    fn foo(&self) {}
}
        "#,
        [("A2", vec![]), ("B2", vec![])],
    );
}

#[test]
fn rustc_issue_106247() {
    check_dyn_compatibility(
        r#"
//- minicore: sync, dispatch_from_dyn
pub trait Trait {
    fn method(&self) where Self: Sync;
}
"#,
        [("Trait", vec![])],
    );
}

#[test]
fn std_error_is_dyn_compatible() {
    check_dyn_compatibility(
        r#"
//- minicore: fmt, dispatch_from_dyn
trait Erased<'a>: 'a {}

pub struct Request<'a>(dyn Erased<'a> + 'a);

pub trait Error: core::fmt::Debug + core::fmt::Display {
    fn provide<'a>(&'a self, request: &mut Request<'a>);
}
"#,
        [("Error", vec![])],
    );
}

#[test]
fn lifetime_gat_is_dyn_incompatible() {
    check_dyn_compatibility(
        r#"
//- minicore: dispatch_from_dyn
trait Foo {
    type Bar<'a>;
}
"#,
        [("Foo", vec![DynCompatibilityViolationKind::GAT])],
    );
}