1 //! This module specifies the input to rust-analyzer. In some sense, this is 2 //! **the** most important module, because all other fancy stuff is strictly 3 //! derived from this input. 4 //! 5 //! Note that neither this module, nor any other part of the analyzer's core do 6 //! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how 7 //! actual IO is done and lowered to input. 8 9 use std::{fmt, mem, ops, panic::RefUnwindSafe, str::FromStr, sync}; 10 11 use cfg::CfgOptions; 12 use la_arena::{Arena, Idx}; 13 use rustc_hash::{FxHashMap, FxHashSet}; 14 use syntax::SmolStr; 15 use triomphe::Arc; 16 use tt::token_id::Subtree; 17 use vfs::{file_set::FileSet, AbsPathBuf, AnchoredPath, FileId, VfsPath}; 18 19 // Map from crate id to the name of the crate and path of the proc-macro. If the value is `None`, 20 // then the crate for the proc-macro hasn't been build yet as the build data is missing. 21 pub type ProcMacroPaths = FxHashMap<CrateId, Result<(Option<String>, AbsPathBuf), String>>; 22 pub type ProcMacros = FxHashMap<CrateId, ProcMacroLoadResult>; 23 24 /// Files are grouped into source roots. A source root is a directory on the 25 /// file systems which is watched for changes. Typically it corresponds to a 26 /// Rust crate. Source roots *might* be nested: in this case, a file belongs to 27 /// the nearest enclosing source root. Paths to files are always relative to a 28 /// source root, and the analyzer does not know the root path of the source root at 29 /// all. So, a file from one source root can't refer to a file in another source 30 /// root by path. 31 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 32 pub struct SourceRootId(pub u32); 33 34 #[derive(Clone, Debug, PartialEq, Eq)] 35 pub struct SourceRoot { 36 /// Sysroot or crates.io library. 37 /// 38 /// Libraries are considered mostly immutable, this assumption is used to 39 /// optimize salsa's query structure 40 pub is_library: bool, 41 file_set: FileSet, 42 } 43 44 impl SourceRoot { new_local(file_set: FileSet) -> SourceRoot45 pub fn new_local(file_set: FileSet) -> SourceRoot { 46 SourceRoot { is_library: false, file_set } 47 } 48 new_library(file_set: FileSet) -> SourceRoot49 pub fn new_library(file_set: FileSet) -> SourceRoot { 50 SourceRoot { is_library: true, file_set } 51 } 52 path_for_file(&self, file: &FileId) -> Option<&VfsPath>53 pub fn path_for_file(&self, file: &FileId) -> Option<&VfsPath> { 54 self.file_set.path_for_file(file) 55 } 56 file_for_path(&self, path: &VfsPath) -> Option<&FileId>57 pub fn file_for_path(&self, path: &VfsPath) -> Option<&FileId> { 58 self.file_set.file_for_path(path) 59 } 60 resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId>61 pub fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> { 62 self.file_set.resolve_path(path) 63 } 64 iter(&self) -> impl Iterator<Item = FileId> + '_65 pub fn iter(&self) -> impl Iterator<Item = FileId> + '_ { 66 self.file_set.iter() 67 } 68 } 69 70 /// `CrateGraph` is a bit of information which turns a set of text files into a 71 /// number of Rust crates. 72 /// 73 /// Each crate is defined by the `FileId` of its root module, the set of enabled 74 /// `cfg` flags and the set of dependencies. 75 /// 76 /// Note that, due to cfg's, there might be several crates for a single `FileId`! 77 /// 78 /// For the purposes of analysis, a crate does not have a name. Instead, names 79 /// are specified on dependency edges. That is, a crate might be known under 80 /// different names in different dependent crates. 81 /// 82 /// Note that `CrateGraph` is build-system agnostic: it's a concept of the Rust 83 /// language proper, not a concept of the build system. In practice, we get 84 /// `CrateGraph` by lowering `cargo metadata` output. 85 /// 86 /// `CrateGraph` is `!Serialize` by design, see 87 /// <https://github.com/rust-lang/rust-analyzer/blob/master/docs/dev/architecture.md#serialization> 88 #[derive(Clone, Default)] 89 pub struct CrateGraph { 90 arena: Arena<CrateData>, 91 } 92 93 impl fmt::Debug for CrateGraph { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 95 f.debug_map() 96 .entries(self.arena.iter().map(|(id, data)| (u32::from(id.into_raw()), data))) 97 .finish() 98 } 99 } 100 101 pub type CrateId = Idx<CrateData>; 102 103 #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] 104 pub struct CrateName(SmolStr); 105 106 impl CrateName { 107 /// Creates a crate name, checking for dashes in the string provided. 108 /// Dashes are not allowed in the crate names, 109 /// hence the input string is returned as `Err` for those cases. new(name: &str) -> Result<CrateName, &str>110 pub fn new(name: &str) -> Result<CrateName, &str> { 111 if name.contains('-') { 112 Err(name) 113 } else { 114 Ok(Self(SmolStr::new(name))) 115 } 116 } 117 118 /// Creates a crate name, unconditionally replacing the dashes with underscores. normalize_dashes(name: &str) -> CrateName119 pub fn normalize_dashes(name: &str) -> CrateName { 120 Self(SmolStr::new(name.replace('-', "_"))) 121 } 122 as_smol_str(&self) -> &SmolStr123 pub fn as_smol_str(&self) -> &SmolStr { 124 &self.0 125 } 126 } 127 128 impl fmt::Display for CrateName { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 130 self.0.fmt(f) 131 } 132 } 133 134 impl ops::Deref for CrateName { 135 type Target = str; deref(&self) -> &str136 fn deref(&self) -> &str { 137 &self.0 138 } 139 } 140 141 /// Origin of the crates. It is used in emitting monikers. 142 #[derive(Debug, Clone, PartialEq, Eq, Hash)] 143 pub enum CrateOrigin { 144 /// Crates that are from the rustc workspace 145 Rustc { name: String }, 146 /// Crates that are workspace members, 147 Local { repo: Option<String>, name: Option<String> }, 148 /// Crates that are non member libraries. 149 Library { repo: Option<String>, name: String }, 150 /// Crates that are provided by the language, like std, core, proc-macro, ... 151 Lang(LangCrateOrigin), 152 } 153 154 impl CrateOrigin { is_local(&self) -> bool155 pub fn is_local(&self) -> bool { 156 matches!(self, CrateOrigin::Local { .. }) 157 } 158 } 159 160 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 161 pub enum LangCrateOrigin { 162 Alloc, 163 Core, 164 ProcMacro, 165 Std, 166 Test, 167 Other, 168 } 169 170 impl From<&str> for LangCrateOrigin { from(s: &str) -> Self171 fn from(s: &str) -> Self { 172 match s { 173 "alloc" => LangCrateOrigin::Alloc, 174 "core" => LangCrateOrigin::Core, 175 "proc-macro" => LangCrateOrigin::ProcMacro, 176 "std" => LangCrateOrigin::Std, 177 "test" => LangCrateOrigin::Test, 178 _ => LangCrateOrigin::Other, 179 } 180 } 181 } 182 183 impl fmt::Display for LangCrateOrigin { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result184 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 185 let text = match self { 186 LangCrateOrigin::Alloc => "alloc", 187 LangCrateOrigin::Core => "core", 188 LangCrateOrigin::ProcMacro => "proc_macro", 189 LangCrateOrigin::Std => "std", 190 LangCrateOrigin::Test => "test", 191 LangCrateOrigin::Other => "other", 192 }; 193 f.write_str(text) 194 } 195 } 196 197 #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] 198 pub struct CrateDisplayName { 199 // The name we use to display various paths (with `_`). 200 crate_name: CrateName, 201 // The name as specified in Cargo.toml (with `-`). 202 canonical_name: String, 203 } 204 205 impl CrateDisplayName { canonical_name(&self) -> &str206 pub fn canonical_name(&self) -> &str { 207 &self.canonical_name 208 } crate_name(&self) -> &CrateName209 pub fn crate_name(&self) -> &CrateName { 210 &self.crate_name 211 } 212 } 213 214 impl From<CrateName> for CrateDisplayName { from(crate_name: CrateName) -> CrateDisplayName215 fn from(crate_name: CrateName) -> CrateDisplayName { 216 let canonical_name = crate_name.to_string(); 217 CrateDisplayName { crate_name, canonical_name } 218 } 219 } 220 221 impl fmt::Display for CrateDisplayName { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 223 self.crate_name.fmt(f) 224 } 225 } 226 227 impl ops::Deref for CrateDisplayName { 228 type Target = str; deref(&self) -> &str229 fn deref(&self) -> &str { 230 &self.crate_name 231 } 232 } 233 234 impl CrateDisplayName { from_canonical_name(canonical_name: String) -> CrateDisplayName235 pub fn from_canonical_name(canonical_name: String) -> CrateDisplayName { 236 let crate_name = CrateName::normalize_dashes(&canonical_name); 237 CrateDisplayName { crate_name, canonical_name } 238 } 239 } 240 241 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 242 pub struct ProcMacroId(pub u32); 243 244 #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] 245 pub enum ProcMacroKind { 246 CustomDerive, 247 FuncLike, 248 Attr, 249 } 250 251 pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe { expand( &self, subtree: &Subtree, attrs: Option<&Subtree>, env: &Env, ) -> Result<Subtree, ProcMacroExpansionError>252 fn expand( 253 &self, 254 subtree: &Subtree, 255 attrs: Option<&Subtree>, 256 env: &Env, 257 ) -> Result<Subtree, ProcMacroExpansionError>; 258 } 259 260 pub enum ProcMacroExpansionError { 261 Panic(String), 262 /// Things like "proc macro server was killed by OOM". 263 System(String), 264 } 265 266 pub type ProcMacroLoadResult = Result<Vec<ProcMacro>, String>; 267 pub type TargetLayoutLoadResult = Result<Arc<str>, Arc<str>>; 268 269 #[derive(Debug, Clone)] 270 pub struct ProcMacro { 271 pub name: SmolStr, 272 pub kind: ProcMacroKind, 273 pub expander: sync::Arc<dyn ProcMacroExpander>, 274 } 275 276 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] 277 pub enum ReleaseChannel { 278 Stable, 279 Beta, 280 Nightly, 281 } 282 283 impl ReleaseChannel { as_str(self) -> &'static str284 pub fn as_str(self) -> &'static str { 285 match self { 286 ReleaseChannel::Stable => "stable", 287 ReleaseChannel::Beta => "beta", 288 ReleaseChannel::Nightly => "nightly", 289 } 290 } 291 from_str(str: &str) -> Option<Self>292 pub fn from_str(str: &str) -> Option<Self> { 293 Some(match str { 294 "" => ReleaseChannel::Stable, 295 "nightly" => ReleaseChannel::Nightly, 296 _ if str.starts_with("beta") => ReleaseChannel::Beta, 297 _ => return None, 298 }) 299 } 300 } 301 302 #[derive(Debug, Clone, PartialEq, Eq)] 303 pub struct CrateData { 304 pub root_file_id: FileId, 305 pub edition: Edition, 306 pub version: Option<String>, 307 /// A name used in the package's project declaration: for Cargo projects, 308 /// its `[package].name` can be different for other project types or even 309 /// absent (a dummy crate for the code snippet, for example). 310 /// 311 /// For purposes of analysis, crates are anonymous (only names in 312 /// `Dependency` matters), this name should only be used for UI. 313 pub display_name: Option<CrateDisplayName>, 314 pub cfg_options: CfgOptions, 315 /// The cfg options that could be used by the crate 316 pub potential_cfg_options: Option<CfgOptions>, 317 pub env: Env, 318 pub dependencies: Vec<Dependency>, 319 pub origin: CrateOrigin, 320 pub is_proc_macro: bool, 321 // FIXME: These things should not be per crate! These are more per workspace crate graph level things 322 pub target_layout: TargetLayoutLoadResult, 323 pub channel: Option<ReleaseChannel>, 324 } 325 326 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 327 pub enum Edition { 328 Edition2015, 329 Edition2018, 330 Edition2021, 331 } 332 333 impl Edition { 334 pub const CURRENT: Edition = Edition::Edition2021; 335 } 336 337 #[derive(Default, Debug, Clone, PartialEq, Eq)] 338 pub struct Env { 339 entries: FxHashMap<String, String>, 340 } 341 342 impl Env { new_for_test_fixture() -> Self343 pub fn new_for_test_fixture() -> Self { 344 Env { 345 entries: FxHashMap::from_iter([( 346 String::from("__ra_is_test_fixture"), 347 String::from("__ra_is_test_fixture"), 348 )]), 349 } 350 } 351 } 352 353 #[derive(Debug, Clone, PartialEq, Eq, Hash)] 354 pub struct Dependency { 355 pub crate_id: CrateId, 356 pub name: CrateName, 357 prelude: bool, 358 } 359 360 impl Dependency { new(name: CrateName, crate_id: CrateId) -> Self361 pub fn new(name: CrateName, crate_id: CrateId) -> Self { 362 Self { name, crate_id, prelude: true } 363 } 364 with_prelude(name: CrateName, crate_id: CrateId, prelude: bool) -> Self365 pub fn with_prelude(name: CrateName, crate_id: CrateId, prelude: bool) -> Self { 366 Self { name, crate_id, prelude } 367 } 368 369 /// Whether this dependency is to be added to the depending crate's extern prelude. is_prelude(&self) -> bool370 pub fn is_prelude(&self) -> bool { 371 self.prelude 372 } 373 } 374 375 impl CrateGraph { add_crate_root( &mut self, root_file_id: FileId, edition: Edition, display_name: Option<CrateDisplayName>, version: Option<String>, cfg_options: CfgOptions, potential_cfg_options: Option<CfgOptions>, env: Env, is_proc_macro: bool, origin: CrateOrigin, target_layout: Result<Arc<str>, Arc<str>>, channel: Option<ReleaseChannel>, ) -> CrateId376 pub fn add_crate_root( 377 &mut self, 378 root_file_id: FileId, 379 edition: Edition, 380 display_name: Option<CrateDisplayName>, 381 version: Option<String>, 382 cfg_options: CfgOptions, 383 potential_cfg_options: Option<CfgOptions>, 384 env: Env, 385 is_proc_macro: bool, 386 origin: CrateOrigin, 387 target_layout: Result<Arc<str>, Arc<str>>, 388 channel: Option<ReleaseChannel>, 389 ) -> CrateId { 390 let data = CrateData { 391 root_file_id, 392 edition, 393 version, 394 display_name, 395 cfg_options, 396 potential_cfg_options, 397 env, 398 dependencies: Vec::new(), 399 origin, 400 target_layout, 401 is_proc_macro, 402 channel, 403 }; 404 self.arena.alloc(data) 405 } 406 407 /// Remove the crate from crate graph. If any crates depend on this crate, the dependency would be replaced 408 /// with the second input. remove_and_replace( &mut self, id: CrateId, replace_with: CrateId, ) -> Result<(), CyclicDependenciesError>409 pub fn remove_and_replace( 410 &mut self, 411 id: CrateId, 412 replace_with: CrateId, 413 ) -> Result<(), CyclicDependenciesError> { 414 for (x, data) in self.arena.iter() { 415 if x == id { 416 continue; 417 } 418 for edge in &data.dependencies { 419 if edge.crate_id == id { 420 self.check_cycle_after_dependency(edge.crate_id, replace_with)?; 421 } 422 } 423 } 424 // if everything was ok, start to replace 425 for (x, data) in self.arena.iter_mut() { 426 if x == id { 427 continue; 428 } 429 for edge in &mut data.dependencies { 430 if edge.crate_id == id { 431 edge.crate_id = replace_with; 432 } 433 } 434 } 435 Ok(()) 436 } 437 add_dep( &mut self, from: CrateId, dep: Dependency, ) -> Result<(), CyclicDependenciesError>438 pub fn add_dep( 439 &mut self, 440 from: CrateId, 441 dep: Dependency, 442 ) -> Result<(), CyclicDependenciesError> { 443 let _p = profile::span("add_dep"); 444 445 self.check_cycle_after_dependency(from, dep.crate_id)?; 446 447 self.arena[from].add_dep(dep); 448 Ok(()) 449 } 450 451 /// Check if adding a dep from `from` to `to` creates a cycle. To figure 452 /// that out, look for a path in the *opposite* direction, from `to` to 453 /// `from`. check_cycle_after_dependency( &self, from: CrateId, to: CrateId, ) -> Result<(), CyclicDependenciesError>454 fn check_cycle_after_dependency( 455 &self, 456 from: CrateId, 457 to: CrateId, 458 ) -> Result<(), CyclicDependenciesError> { 459 if let Some(path) = self.find_path(&mut FxHashSet::default(), to, from) { 460 let path = path.into_iter().map(|it| (it, self[it].display_name.clone())).collect(); 461 let err = CyclicDependenciesError { path }; 462 assert!(err.from().0 == from && err.to().0 == to); 463 return Err(err); 464 } 465 Ok(()) 466 } 467 is_empty(&self) -> bool468 pub fn is_empty(&self) -> bool { 469 self.arena.is_empty() 470 } 471 iter(&self) -> impl Iterator<Item = CrateId> + '_472 pub fn iter(&self) -> impl Iterator<Item = CrateId> + '_ { 473 self.arena.iter().map(|(idx, _)| idx) 474 } 475 476 // FIXME: used for `handle_hack_cargo_workspace`, should be removed later 477 #[doc(hidden)] iter_mut(&mut self) -> impl Iterator<Item = (CrateId, &mut CrateData)> + '_478 pub fn iter_mut(&mut self) -> impl Iterator<Item = (CrateId, &mut CrateData)> + '_ { 479 self.arena.iter_mut() 480 } 481 482 /// Returns an iterator over all transitive dependencies of the given crate, 483 /// including the crate itself. transitive_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId>484 pub fn transitive_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId> { 485 let mut worklist = vec![of]; 486 let mut deps = FxHashSet::default(); 487 488 while let Some(krate) = worklist.pop() { 489 if !deps.insert(krate) { 490 continue; 491 } 492 493 worklist.extend(self[krate].dependencies.iter().map(|dep| dep.crate_id)); 494 } 495 496 deps.into_iter() 497 } 498 499 /// Returns all transitive reverse dependencies of the given crate, 500 /// including the crate itself. transitive_rev_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId>501 pub fn transitive_rev_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId> { 502 let mut worklist = vec![of]; 503 let mut rev_deps = FxHashSet::default(); 504 rev_deps.insert(of); 505 506 let mut inverted_graph = FxHashMap::<_, Vec<_>>::default(); 507 self.arena.iter().for_each(|(krate, data)| { 508 data.dependencies 509 .iter() 510 .for_each(|dep| inverted_graph.entry(dep.crate_id).or_default().push(krate)) 511 }); 512 513 while let Some(krate) = worklist.pop() { 514 if let Some(krate_rev_deps) = inverted_graph.get(&krate) { 515 krate_rev_deps 516 .iter() 517 .copied() 518 .filter(|&rev_dep| rev_deps.insert(rev_dep)) 519 .for_each(|rev_dep| worklist.push(rev_dep)); 520 } 521 } 522 523 rev_deps.into_iter() 524 } 525 526 /// Returns all crates in the graph, sorted in topological order (ie. dependencies of a crate 527 /// come before the crate itself). crates_in_topological_order(&self) -> Vec<CrateId>528 pub fn crates_in_topological_order(&self) -> Vec<CrateId> { 529 let mut res = Vec::new(); 530 let mut visited = FxHashSet::default(); 531 532 for krate in self.iter() { 533 go(self, &mut visited, &mut res, krate); 534 } 535 536 return res; 537 538 fn go( 539 graph: &CrateGraph, 540 visited: &mut FxHashSet<CrateId>, 541 res: &mut Vec<CrateId>, 542 source: CrateId, 543 ) { 544 if !visited.insert(source) { 545 return; 546 } 547 for dep in graph[source].dependencies.iter() { 548 go(graph, visited, res, dep.crate_id) 549 } 550 res.push(source) 551 } 552 } 553 554 // FIXME: this only finds one crate with the given root; we could have multiple crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId>555 pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> { 556 let (crate_id, _) = 557 self.arena.iter().find(|(_crate_id, data)| data.root_file_id == file_id)?; 558 Some(crate_id) 559 } 560 sort_deps(&mut self)561 pub fn sort_deps(&mut self) { 562 self.arena 563 .iter_mut() 564 .for_each(|(_, data)| data.dependencies.sort_by_key(|dep| dep.crate_id)); 565 } 566 567 /// Extends this crate graph by adding a complete disjoint second crate 568 /// graph and adjust the ids in the [`ProcMacroPaths`] accordingly. 569 /// 570 /// This will deduplicate the crates of the graph where possible. 571 /// Note that for deduplication to fully work, `self`'s crate dependencies must be sorted by crate id. 572 /// If the crate dependencies were sorted, the resulting graph from this `extend` call will also have the crate dependencies sorted. extend(&mut self, mut other: CrateGraph, proc_macros: &mut ProcMacroPaths)573 pub fn extend(&mut self, mut other: CrateGraph, proc_macros: &mut ProcMacroPaths) { 574 let topo = other.crates_in_topological_order(); 575 let mut id_map: FxHashMap<CrateId, CrateId> = FxHashMap::default(); 576 577 for topo in topo { 578 let crate_data = &mut other.arena[topo]; 579 crate_data.dependencies.iter_mut().for_each(|dep| dep.crate_id = id_map[&dep.crate_id]); 580 crate_data.dependencies.sort_by_key(|dep| dep.crate_id); 581 582 let res = self.arena.iter().find_map( 583 |(id, data)| { 584 if data == crate_data { 585 Some(id) 586 } else { 587 None 588 } 589 }, 590 ); 591 if let Some(res) = res { 592 id_map.insert(topo, res); 593 } else { 594 let id = self.arena.alloc(crate_data.clone()); 595 id_map.insert(topo, id); 596 } 597 } 598 599 *proc_macros = 600 mem::take(proc_macros).into_iter().map(|(id, macros)| (id_map[&id], macros)).collect(); 601 } 602 find_path( &self, visited: &mut FxHashSet<CrateId>, from: CrateId, to: CrateId, ) -> Option<Vec<CrateId>>603 fn find_path( 604 &self, 605 visited: &mut FxHashSet<CrateId>, 606 from: CrateId, 607 to: CrateId, 608 ) -> Option<Vec<CrateId>> { 609 if !visited.insert(from) { 610 return None; 611 } 612 613 if from == to { 614 return Some(vec![to]); 615 } 616 617 for dep in &self[from].dependencies { 618 let crate_id = dep.crate_id; 619 if let Some(mut path) = self.find_path(visited, crate_id, to) { 620 path.push(from); 621 return Some(path); 622 } 623 } 624 625 None 626 } 627 628 // Work around for https://github.com/rust-lang/rust-analyzer/issues/6038. 629 // As hacky as it gets. patch_cfg_if(&mut self) -> bool630 pub fn patch_cfg_if(&mut self) -> bool { 631 // we stupidly max by version in an attempt to have all duplicated std's depend on the same cfg_if so that deduplication still works 632 let cfg_if = 633 self.hacky_find_crate("cfg_if").max_by_key(|&it| self.arena[it].version.clone()); 634 let std = self.hacky_find_crate("std").next(); 635 match (cfg_if, std) { 636 (Some(cfg_if), Some(std)) => { 637 self.arena[cfg_if].dependencies.clear(); 638 self.arena[std] 639 .dependencies 640 .push(Dependency::new(CrateName::new("cfg_if").unwrap(), cfg_if)); 641 true 642 } 643 _ => false, 644 } 645 } 646 hacky_find_crate<'a>(&'a self, display_name: &'a str) -> impl Iterator<Item = CrateId> + 'a647 fn hacky_find_crate<'a>(&'a self, display_name: &'a str) -> impl Iterator<Item = CrateId> + 'a { 648 self.iter().filter(move |it| self[*it].display_name.as_deref() == Some(display_name)) 649 } 650 } 651 652 impl ops::Index<CrateId> for CrateGraph { 653 type Output = CrateData; index(&self, crate_id: CrateId) -> &CrateData654 fn index(&self, crate_id: CrateId) -> &CrateData { 655 &self.arena[crate_id] 656 } 657 } 658 659 impl CrateData { add_dep(&mut self, dep: Dependency)660 fn add_dep(&mut self, dep: Dependency) { 661 self.dependencies.push(dep) 662 } 663 } 664 665 impl FromStr for Edition { 666 type Err = ParseEditionError; 667 from_str(s: &str) -> Result<Self, Self::Err>668 fn from_str(s: &str) -> Result<Self, Self::Err> { 669 let res = match s { 670 "2015" => Edition::Edition2015, 671 "2018" => Edition::Edition2018, 672 "2021" => Edition::Edition2021, 673 _ => return Err(ParseEditionError { invalid_input: s.to_string() }), 674 }; 675 Ok(res) 676 } 677 } 678 679 impl fmt::Display for Edition { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result680 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 681 f.write_str(match self { 682 Edition::Edition2015 => "2015", 683 Edition::Edition2018 => "2018", 684 Edition::Edition2021 => "2021", 685 }) 686 } 687 } 688 689 impl FromIterator<(String, String)> for Env { from_iter<T: IntoIterator<Item = (String, String)>>(iter: T) -> Self690 fn from_iter<T: IntoIterator<Item = (String, String)>>(iter: T) -> Self { 691 Env { entries: FromIterator::from_iter(iter) } 692 } 693 } 694 695 impl Env { set(&mut self, env: &str, value: String)696 pub fn set(&mut self, env: &str, value: String) { 697 self.entries.insert(env.to_owned(), value); 698 } 699 get(&self, env: &str) -> Option<String>700 pub fn get(&self, env: &str) -> Option<String> { 701 self.entries.get(env).cloned() 702 } 703 iter(&self) -> impl Iterator<Item = (&str, &str)>704 pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> { 705 self.entries.iter().map(|(k, v)| (k.as_str(), v.as_str())) 706 } 707 } 708 709 #[derive(Debug)] 710 pub struct ParseEditionError { 711 invalid_input: String, 712 } 713 714 impl fmt::Display for ParseEditionError { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result715 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 716 write!(f, "invalid edition: {:?}", self.invalid_input) 717 } 718 } 719 720 impl std::error::Error for ParseEditionError {} 721 722 #[derive(Debug)] 723 pub struct CyclicDependenciesError { 724 path: Vec<(CrateId, Option<CrateDisplayName>)>, 725 } 726 727 impl CyclicDependenciesError { from(&self) -> &(CrateId, Option<CrateDisplayName>)728 fn from(&self) -> &(CrateId, Option<CrateDisplayName>) { 729 self.path.first().unwrap() 730 } to(&self) -> &(CrateId, Option<CrateDisplayName>)731 fn to(&self) -> &(CrateId, Option<CrateDisplayName>) { 732 self.path.last().unwrap() 733 } 734 } 735 736 impl fmt::Display for CyclicDependenciesError { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result737 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 738 let render = |(id, name): &(CrateId, Option<CrateDisplayName>)| match name { 739 Some(it) => format!("{it}({id:?})"), 740 None => format!("{id:?}"), 741 }; 742 let path = self.path.iter().rev().map(render).collect::<Vec<String>>().join(" -> "); 743 write!( 744 f, 745 "cyclic deps: {} -> {}, alternative path: {}", 746 render(self.from()), 747 render(self.to()), 748 path 749 ) 750 } 751 } 752 753 #[cfg(test)] 754 mod tests { 755 use crate::CrateOrigin; 756 757 use super::{CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId}; 758 759 #[test] detect_cyclic_dependency_indirect()760 fn detect_cyclic_dependency_indirect() { 761 let mut graph = CrateGraph::default(); 762 let crate1 = graph.add_crate_root( 763 FileId(1u32), 764 Edition2018, 765 None, 766 None, 767 Default::default(), 768 Default::default(), 769 Env::default(), 770 false, 771 CrateOrigin::Local { repo: None, name: None }, 772 Err("".into()), 773 None, 774 ); 775 let crate2 = graph.add_crate_root( 776 FileId(2u32), 777 Edition2018, 778 None, 779 None, 780 Default::default(), 781 Default::default(), 782 Env::default(), 783 false, 784 CrateOrigin::Local { repo: None, name: None }, 785 Err("".into()), 786 None, 787 ); 788 let crate3 = graph.add_crate_root( 789 FileId(3u32), 790 Edition2018, 791 None, 792 None, 793 Default::default(), 794 Default::default(), 795 Env::default(), 796 false, 797 CrateOrigin::Local { repo: None, name: None }, 798 Err("".into()), 799 None, 800 ); 801 assert!(graph 802 .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) 803 .is_ok()); 804 assert!(graph 805 .add_dep(crate2, Dependency::new(CrateName::new("crate3").unwrap(), crate3)) 806 .is_ok()); 807 assert!(graph 808 .add_dep(crate3, Dependency::new(CrateName::new("crate1").unwrap(), crate1)) 809 .is_err()); 810 } 811 812 #[test] detect_cyclic_dependency_direct()813 fn detect_cyclic_dependency_direct() { 814 let mut graph = CrateGraph::default(); 815 let crate1 = graph.add_crate_root( 816 FileId(1u32), 817 Edition2018, 818 None, 819 None, 820 Default::default(), 821 Default::default(), 822 Env::default(), 823 false, 824 CrateOrigin::Local { repo: None, name: None }, 825 Err("".into()), 826 None, 827 ); 828 let crate2 = graph.add_crate_root( 829 FileId(2u32), 830 Edition2018, 831 None, 832 None, 833 Default::default(), 834 Default::default(), 835 Env::default(), 836 false, 837 CrateOrigin::Local { repo: None, name: None }, 838 Err("".into()), 839 None, 840 ); 841 assert!(graph 842 .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) 843 .is_ok()); 844 assert!(graph 845 .add_dep(crate2, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) 846 .is_err()); 847 } 848 849 #[test] it_works()850 fn it_works() { 851 let mut graph = CrateGraph::default(); 852 let crate1 = graph.add_crate_root( 853 FileId(1u32), 854 Edition2018, 855 None, 856 None, 857 Default::default(), 858 Default::default(), 859 Env::default(), 860 false, 861 CrateOrigin::Local { repo: None, name: None }, 862 Err("".into()), 863 None, 864 ); 865 let crate2 = graph.add_crate_root( 866 FileId(2u32), 867 Edition2018, 868 None, 869 None, 870 Default::default(), 871 Default::default(), 872 Env::default(), 873 false, 874 CrateOrigin::Local { repo: None, name: None }, 875 Err("".into()), 876 None, 877 ); 878 let crate3 = graph.add_crate_root( 879 FileId(3u32), 880 Edition2018, 881 None, 882 None, 883 Default::default(), 884 Default::default(), 885 Env::default(), 886 false, 887 CrateOrigin::Local { repo: None, name: None }, 888 Err("".into()), 889 None, 890 ); 891 assert!(graph 892 .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) 893 .is_ok()); 894 assert!(graph 895 .add_dep(crate2, Dependency::new(CrateName::new("crate3").unwrap(), crate3)) 896 .is_ok()); 897 } 898 899 #[test] dashes_are_normalized()900 fn dashes_are_normalized() { 901 let mut graph = CrateGraph::default(); 902 let crate1 = graph.add_crate_root( 903 FileId(1u32), 904 Edition2018, 905 None, 906 None, 907 Default::default(), 908 Default::default(), 909 Env::default(), 910 false, 911 CrateOrigin::Local { repo: None, name: None }, 912 Err("".into()), 913 None, 914 ); 915 let crate2 = graph.add_crate_root( 916 FileId(2u32), 917 Edition2018, 918 None, 919 None, 920 Default::default(), 921 Default::default(), 922 Env::default(), 923 false, 924 CrateOrigin::Local { repo: None, name: None }, 925 Err("".into()), 926 None, 927 ); 928 assert!(graph 929 .add_dep( 930 crate1, 931 Dependency::new(CrateName::normalize_dashes("crate-name-with-dashes"), crate2) 932 ) 933 .is_ok()); 934 assert_eq!( 935 graph[crate1].dependencies, 936 vec![Dependency::new(CrateName::new("crate_name_with_dashes").unwrap(), crate2)] 937 ); 938 } 939 } 940