|
8 | 8 |
|
9 | 9 | use std::{fmt, mem, ops, panic::RefUnwindSafe, str::FromStr, sync};
|
10 | 10 |
|
11 |
| -use cfg::CfgOptions; |
| 11 | +use cfg::{CfgDiff, CfgOptions}; |
12 | 12 | use la_arena::{Arena, Idx};
|
13 | 13 | use rustc_hash::{FxHashMap, FxHashSet};
|
14 | 14 | use syntax::SmolStr;
|
@@ -155,6 +155,10 @@ impl CrateOrigin {
|
155 | 155 | pub fn is_local(&self) -> bool {
|
156 | 156 | matches!(self, CrateOrigin::Local { .. })
|
157 | 157 | }
|
| 158 | + |
| 159 | + pub fn is_lib(&self) -> bool { |
| 160 | + matches!(self, CrateOrigin::Library { .. }) |
| 161 | + } |
158 | 162 | }
|
159 | 163 |
|
160 | 164 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
@@ -324,6 +328,99 @@ pub struct CrateData {
|
324 | 328 | pub channel: Option<ReleaseChannel>,
|
325 | 329 | }
|
326 | 330 |
|
| 331 | +impl CrateData { |
| 332 | + /** |
| 333 | + Check if [`other`] is almost equal to [`self`]. |
| 334 | + This method has some obscure bits. These are mostly there to be compliant with |
| 335 | + some patches. References to the patches are given. |
| 336 | + */ |
| 337 | + pub fn almost_eq(&self, other: &CrateData) -> bool { |
| 338 | + if self.root_file_id != other.root_file_id { |
| 339 | + return false; |
| 340 | + } |
| 341 | + |
| 342 | + if self.display_name != other.display_name { |
| 343 | + return false; |
| 344 | + } |
| 345 | + |
| 346 | + if self.is_proc_macro != other.is_proc_macro { |
| 347 | + return false; |
| 348 | + } |
| 349 | + |
| 350 | + if self.edition != other.edition { |
| 351 | + return false; |
| 352 | + } |
| 353 | + |
| 354 | + if self.version != other.version { |
| 355 | + return false; |
| 356 | + } |
| 357 | + |
| 358 | + let mut opts = self.cfg_options.clone(); |
| 359 | + opts.apply_diff(CfgDiff { |
| 360 | + disable: other.cfg_options.clone().into_iter().collect(), |
| 361 | + enable: vec![], |
| 362 | + }); |
| 363 | + |
| 364 | + let mut cfgs = opts.into_iter(); |
| 365 | + if let Some(cfg) = cfgs.next() { |
| 366 | + // Don't care if rust_analyzer CfgAtom is the only cfg in the difference set of self's and other's cfgs. |
| 367 | + // https://github.com/rust-lang/rust-analyzer/blob/0840038f02daec6ba3238f05d8caa037d28701a0/crates/project-model/src/workspace.rs#L894 |
| 368 | + if !cfgs.next().is_none() || cfg.to_string() != "rust_analyzer" { |
| 369 | + return false; |
| 370 | + } |
| 371 | + } |
| 372 | + |
| 373 | + let mut itself = self.dependencies.iter(); |
| 374 | + let mut otself = other.dependencies.iter(); |
| 375 | + let (mut anx, mut bnx) = (itself.next(), otself.next()); |
| 376 | + loop { |
| 377 | + match (anx, bnx) { |
| 378 | + (None, None) => { |
| 379 | + break; |
| 380 | + } |
| 381 | + (None, Some(b)) => { |
| 382 | + if b.kind != DependencyKind::Normal { |
| 383 | + bnx = otself.next(); |
| 384 | + } else { |
| 385 | + break; |
| 386 | + } |
| 387 | + } |
| 388 | + (Some(a), None) => { |
| 389 | + if a.kind != DependencyKind::Normal { |
| 390 | + anx = itself.next(); |
| 391 | + } else { |
| 392 | + break; |
| 393 | + } |
| 394 | + } |
| 395 | + (Some(a), Some(b)) => { |
| 396 | + if a.kind != DependencyKind::Normal { |
| 397 | + anx = itself.next(); |
| 398 | + continue; |
| 399 | + } |
| 400 | + |
| 401 | + if b.kind != DependencyKind::Normal { |
| 402 | + bnx = otself.next(); |
| 403 | + continue; |
| 404 | + } |
| 405 | + |
| 406 | + if a != b { |
| 407 | + return false; |
| 408 | + } |
| 409 | + |
| 410 | + anx = itself.next(); |
| 411 | + bnx = otself.next(); |
| 412 | + } |
| 413 | + } |
| 414 | + } |
| 415 | + |
| 416 | + if self.env != other.env { |
| 417 | + return false; |
| 418 | + } |
| 419 | + |
| 420 | + true |
| 421 | + } |
| 422 | +} |
| 423 | + |
327 | 424 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
328 | 425 | pub enum Edition {
|
329 | 426 | Edition2015,
|
@@ -351,26 +448,43 @@ impl Env {
|
351 | 448 | }
|
352 | 449 | }
|
353 | 450 |
|
| 451 | +#[derive(Debug, Clone, PartialEq, Eq, Hash)] |
| 452 | +pub enum DependencyKind { |
| 453 | + Normal, |
| 454 | + Dev, |
| 455 | + Build, |
| 456 | +} |
| 457 | + |
354 | 458 | #[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
355 | 459 | pub struct Dependency {
|
356 | 460 | pub crate_id: CrateId,
|
357 | 461 | pub name: CrateName,
|
| 462 | + kind: DependencyKind, |
358 | 463 | prelude: bool,
|
359 | 464 | }
|
360 | 465 |
|
361 | 466 | impl Dependency {
|
362 |
| - pub fn new(name: CrateName, crate_id: CrateId) -> Self { |
363 |
| - Self { name, crate_id, prelude: true } |
| 467 | + pub fn new(name: CrateName, crate_id: CrateId, kind: DependencyKind) -> Self { |
| 468 | + Self { name, crate_id, prelude: true, kind } |
364 | 469 | }
|
365 | 470 |
|
366 |
| - pub fn with_prelude(name: CrateName, crate_id: CrateId, prelude: bool) -> Self { |
367 |
| - Self { name, crate_id, prelude } |
| 471 | + pub fn with_prelude( |
| 472 | + name: CrateName, |
| 473 | + crate_id: CrateId, |
| 474 | + prelude: bool, |
| 475 | + kind: DependencyKind, |
| 476 | + ) -> Self { |
| 477 | + Self { name, crate_id, prelude, kind } |
368 | 478 | }
|
369 | 479 |
|
370 | 480 | /// Whether this dependency is to be added to the depending crate's extern prelude.
|
371 | 481 | pub fn is_prelude(&self) -> bool {
|
372 | 482 | self.prelude
|
373 | 483 | }
|
| 484 | + |
| 485 | + pub fn kind(&self) -> &DependencyKind { |
| 486 | + &self.kind |
| 487 | + } |
374 | 488 | }
|
375 | 489 |
|
376 | 490 | impl CrateGraph {
|
@@ -572,25 +686,41 @@ impl CrateGraph {
|
572 | 686 | /// Note that for deduplication to fully work, `self`'s crate dependencies must be sorted by crate id.
|
573 | 687 | /// If the crate dependencies were sorted, the resulting graph from this `extend` call will also have the crate dependencies sorted.
|
574 | 688 | pub fn extend(&mut self, mut other: CrateGraph, proc_macros: &mut ProcMacroPaths) {
|
| 689 | + enum ExtendStrategy { |
| 690 | + Dedup(CrateId), |
| 691 | + Replace(CrateId), |
| 692 | + } |
| 693 | + |
575 | 694 | let topo = other.crates_in_topological_order();
|
576 | 695 | let mut id_map: FxHashMap<CrateId, CrateId> = FxHashMap::default();
|
577 |
| - |
578 | 696 | for topo in topo {
|
579 | 697 | let crate_data = &mut other.arena[topo];
|
580 |
| - crate_data.dependencies.iter_mut().for_each(|dep| dep.crate_id = id_map[&dep.crate_id]); |
581 |
| - crate_data.dependencies.sort_by_key(|dep| dep.crate_id); |
582 | 698 |
|
583 |
| - let res = self.arena.iter().find_map( |
584 |
| - |(id, data)| { |
585 |
| - if data == crate_data { |
586 |
| - Some(id) |
587 |
| - } else { |
588 |
| - None |
| 699 | + crate_data.dependencies.iter_mut().for_each(|dep| { |
| 700 | + dep.crate_id = id_map[&dep.crate_id]; |
| 701 | + }); |
| 702 | + crate_data.dependencies.sort_by_key(|dep| dep.crate_id); |
| 703 | + let res = self.arena.iter().find_map(|(id, data)| { |
| 704 | + if data.almost_eq(crate_data) { |
| 705 | + if data.origin.is_lib() && crate_data.origin.is_local() { |
| 706 | + // See #15656 for a relevant example. |
| 707 | + return Some(ExtendStrategy::Replace(id)); |
589 | 708 | }
|
590 |
| - }, |
591 |
| - ); |
| 709 | + |
| 710 | + return Some(ExtendStrategy::Dedup(id)); |
| 711 | + } |
| 712 | + None |
| 713 | + }); |
| 714 | + |
592 | 715 | if let Some(res) = res {
|
593 |
| - id_map.insert(topo, res); |
| 716 | + match res { |
| 717 | + ExtendStrategy::Dedup(res) => id_map.insert(topo, res), |
| 718 | + ExtendStrategy::Replace(res) => { |
| 719 | + let id = self.arena.alloc(crate_data.clone()); |
| 720 | + let _ = self.remove_and_replace(res, id); |
| 721 | + id_map.insert(topo, id) |
| 722 | + } |
| 723 | + }; |
594 | 724 | } else {
|
595 | 725 | let id = self.arena.alloc(crate_data.clone());
|
596 | 726 | id_map.insert(topo, id);
|
@@ -636,9 +766,11 @@ impl CrateGraph {
|
636 | 766 | match (cfg_if, std) {
|
637 | 767 | (Some(cfg_if), Some(std)) => {
|
638 | 768 | self.arena[cfg_if].dependencies.clear();
|
639 |
| - self.arena[std] |
640 |
| - .dependencies |
641 |
| - .push(Dependency::new(CrateName::new("cfg_if").unwrap(), cfg_if)); |
| 769 | + self.arena[std].dependencies.push(Dependency::new( |
| 770 | + CrateName::new("cfg_if").unwrap(), |
| 771 | + cfg_if, |
| 772 | + DependencyKind::Normal, |
| 773 | + )); |
642 | 774 | true
|
643 | 775 | }
|
644 | 776 | _ => false,
|
@@ -759,7 +891,7 @@ impl fmt::Display for CyclicDependenciesError {
|
759 | 891 |
|
760 | 892 | #[cfg(test)]
|
761 | 893 | mod tests {
|
762 |
| - use crate::CrateOrigin; |
| 894 | + use crate::{CrateOrigin, DependencyKind}; |
763 | 895 |
|
764 | 896 | use super::{CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId};
|
765 | 897 |
|
@@ -806,13 +938,22 @@ mod tests {
|
806 | 938 | None,
|
807 | 939 | );
|
808 | 940 | assert!(graph
|
809 |
| - .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) |
| 941 | + .add_dep( |
| 942 | + crate1, |
| 943 | + Dependency::new(CrateName::new("crate2").unwrap(), crate2, DependencyKind::Normal) |
| 944 | + ) |
810 | 945 | .is_ok());
|
811 | 946 | assert!(graph
|
812 |
| - .add_dep(crate2, Dependency::new(CrateName::new("crate3").unwrap(), crate3)) |
| 947 | + .add_dep( |
| 948 | + crate2, |
| 949 | + Dependency::new(CrateName::new("crate3").unwrap(), crate3, DependencyKind::Normal) |
| 950 | + ) |
813 | 951 | .is_ok());
|
814 | 952 | assert!(graph
|
815 |
| - .add_dep(crate3, Dependency::new(CrateName::new("crate1").unwrap(), crate1)) |
| 953 | + .add_dep( |
| 954 | + crate3, |
| 955 | + Dependency::new(CrateName::new("crate1").unwrap(), crate1, DependencyKind::Normal) |
| 956 | + ) |
816 | 957 | .is_err());
|
817 | 958 | }
|
818 | 959 |
|
@@ -846,10 +987,16 @@ mod tests {
|
846 | 987 | None,
|
847 | 988 | );
|
848 | 989 | assert!(graph
|
849 |
| - .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) |
| 990 | + .add_dep( |
| 991 | + crate1, |
| 992 | + Dependency::new(CrateName::new("crate2").unwrap(), crate2, DependencyKind::Normal) |
| 993 | + ) |
850 | 994 | .is_ok());
|
851 | 995 | assert!(graph
|
852 |
| - .add_dep(crate2, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) |
| 996 | + .add_dep( |
| 997 | + crate2, |
| 998 | + Dependency::new(CrateName::new("crate2").unwrap(), crate2, DependencyKind::Normal) |
| 999 | + ) |
853 | 1000 | .is_err());
|
854 | 1001 | }
|
855 | 1002 |
|
@@ -896,10 +1043,16 @@ mod tests {
|
896 | 1043 | None,
|
897 | 1044 | );
|
898 | 1045 | assert!(graph
|
899 |
| - .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) |
| 1046 | + .add_dep( |
| 1047 | + crate1, |
| 1048 | + Dependency::new(CrateName::new("crate2").unwrap(), crate2, DependencyKind::Normal) |
| 1049 | + ) |
900 | 1050 | .is_ok());
|
901 | 1051 | assert!(graph
|
902 |
| - .add_dep(crate2, Dependency::new(CrateName::new("crate3").unwrap(), crate3)) |
| 1052 | + .add_dep( |
| 1053 | + crate2, |
| 1054 | + Dependency::new(CrateName::new("crate3").unwrap(), crate3, DependencyKind::Normal) |
| 1055 | + ) |
903 | 1056 | .is_ok());
|
904 | 1057 | }
|
905 | 1058 |
|
@@ -935,12 +1088,20 @@ mod tests {
|
935 | 1088 | assert!(graph
|
936 | 1089 | .add_dep(
|
937 | 1090 | crate1,
|
938 |
| - Dependency::new(CrateName::normalize_dashes("crate-name-with-dashes"), crate2) |
| 1091 | + Dependency::new( |
| 1092 | + CrateName::normalize_dashes("crate-name-with-dashes"), |
| 1093 | + crate2, |
| 1094 | + DependencyKind::Normal |
| 1095 | + ) |
939 | 1096 | )
|
940 | 1097 | .is_ok());
|
941 | 1098 | assert_eq!(
|
942 | 1099 | graph[crate1].dependencies,
|
943 |
| - vec![Dependency::new(CrateName::new("crate_name_with_dashes").unwrap(), crate2)] |
| 1100 | + vec![Dependency::new( |
| 1101 | + CrateName::new("crate_name_with_dashes").unwrap(), |
| 1102 | + crate2, |
| 1103 | + DependencyKind::Normal |
| 1104 | + )] |
944 | 1105 | );
|
945 | 1106 | }
|
946 | 1107 | }
|
0 commit comments