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.
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.
9 use std
::{fmt, ops, panic::RefUnwindSafe, str::FromStr, sync::Arc}
;
12 use rustc_hash
::FxHashMap
;
13 use stdx
::hash
::{NoHashHashMap, NoHashHashSet}
;
16 use vfs
::{file_set::FileSet, AnchoredPath, FileId, VfsPath}
;
18 /// Files are grouped into source roots. A source root is a directory on the
19 /// file systems which is watched for changes. Typically it corresponds to a
20 /// Rust crate. Source roots *might* be nested: in this case, a file belongs to
21 /// the nearest enclosing source root. Paths to files are always relative to a
22 /// source root, and the analyzer does not know the root path of the source root at
23 /// all. So, a file from one source root can't refer to a file in another source
25 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
26 pub struct SourceRootId(pub u32);
28 #[derive(Clone, Debug, PartialEq, Eq)]
29 pub struct SourceRoot
{
30 /// Sysroot or crates.io library.
32 /// Libraries are considered mostly immutable, this assumption is used to
33 /// optimize salsa's query structure
39 pub fn new_local(file_set
: FileSet
) -> SourceRoot
{
40 SourceRoot { is_library: false, file_set }
43 pub fn new_library(file_set
: FileSet
) -> SourceRoot
{
44 SourceRoot { is_library: true, file_set }
47 pub fn path_for_file(&self, file
: &FileId
) -> Option
<&VfsPath
> {
48 self.file_set
.path_for_file(file
)
51 pub fn file_for_path(&self, path
: &VfsPath
) -> Option
<&FileId
> {
52 self.file_set
.file_for_path(path
)
55 pub fn resolve_path(&self, path
: AnchoredPath
<'_
>) -> Option
<FileId
> {
56 self.file_set
.resolve_path(path
)
59 pub fn iter(&self) -> impl Iterator
<Item
= FileId
> + '_
{
64 /// `CrateGraph` is a bit of information which turns a set of text files into a
65 /// number of Rust crates.
67 /// Each crate is defined by the `FileId` of its root module, the set of enabled
68 /// `cfg` flags and the set of dependencies.
70 /// Note that, due to cfg's, there might be several crates for a single `FileId`!
72 /// For the purposes of analysis, a crate does not have a name. Instead, names
73 /// are specified on dependency edges. That is, a crate might be known under
74 /// different names in different dependent crates.
76 /// Note that `CrateGraph` is build-system agnostic: it's a concept of the Rust
77 /// language proper, not a concept of the build system. In practice, we get
78 /// `CrateGraph` by lowering `cargo metadata` output.
80 /// `CrateGraph` is `!Serialize` by design, see
81 /// <https://github.com/rust-lang/rust-analyzer/blob/master/docs/dev/architecture.md#serialization>
82 #[derive(Debug, Clone, Default /* Serialize, Deserialize */)]
83 pub struct CrateGraph
{
84 arena
: NoHashHashMap
<CrateId
, CrateData
>,
87 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
88 pub struct CrateId(pub u32);
90 impl stdx
::hash
::NoHashHashable
for CrateId {}
91 impl std
::hash
::Hash
for CrateId
{
92 fn hash
<H
: std
::hash
::Hasher
>(&self, state
: &mut H
) {
97 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
98 pub struct CrateName(SmolStr
);
101 /// Creates a crate name, checking for dashes in the string provided.
102 /// Dashes are not allowed in the crate names,
103 /// hence the input string is returned as `Err` for those cases.
104 pub fn new(name
: &str) -> Result
<CrateName
, &str> {
105 if name
.contains('
-'
) {
108 Ok(Self(SmolStr
::new(name
)))
112 /// Creates a crate name, unconditionally replacing the dashes with underscores.
113 pub fn normalize_dashes(name
: &str) -> CrateName
{
114 Self(SmolStr
::new(name
.replace('
-'
, "_")))
117 pub fn as_smol_str(&self) -> &SmolStr
{
122 impl fmt
::Display
for CrateName
{
123 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
128 impl ops
::Deref
for CrateName
{
130 fn deref(&self) -> &str {
135 /// Origin of the crates. It is used in emitting monikers.
136 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
137 pub enum CrateOrigin
{
138 /// Crates that are from crates.io official registry,
139 CratesIo { repo: Option<String> }
,
140 /// Crates that are provided by the language, like std, core, proc-macro, ...
141 Lang(LangCrateOrigin
),
144 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
145 pub enum LangCrateOrigin
{
154 impl From
<&str> for LangCrateOrigin
{
155 fn from(s
: &str) -> Self {
157 "alloc" => LangCrateOrigin
::Alloc
,
158 "core" => LangCrateOrigin
::Core
,
159 "proc-macro" => LangCrateOrigin
::ProcMacro
,
160 "std" => LangCrateOrigin
::Std
,
161 "test" => LangCrateOrigin
::Test
,
162 _
=> LangCrateOrigin
::Other
,
167 impl fmt
::Display
for LangCrateOrigin
{
168 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
169 let text
= match self {
170 LangCrateOrigin
::Alloc
=> "alloc",
171 LangCrateOrigin
::Core
=> "core",
172 LangCrateOrigin
::ProcMacro
=> "proc_macro",
173 LangCrateOrigin
::Std
=> "std",
174 LangCrateOrigin
::Test
=> "test",
175 LangCrateOrigin
::Other
=> "other",
181 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
182 pub struct CrateDisplayName
{
183 // The name we use to display various paths (with `_`).
184 crate_name
: CrateName
,
185 // The name as specified in Cargo.toml (with `-`).
186 canonical_name
: String
,
189 impl CrateDisplayName
{
190 pub fn canonical_name(&self) -> &str {
193 pub fn crate_name(&self) -> &CrateName
{
198 impl From
<CrateName
> for CrateDisplayName
{
199 fn from(crate_name
: CrateName
) -> CrateDisplayName
{
200 let canonical_name
= crate_name
.to_string();
201 CrateDisplayName { crate_name, canonical_name }
205 impl fmt
::Display
for CrateDisplayName
{
206 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
207 self.crate_name
.fmt(f
)
211 impl ops
::Deref
for CrateDisplayName
{
213 fn deref(&self) -> &str {
218 impl CrateDisplayName
{
219 pub fn from_canonical_name(canonical_name
: String
) -> CrateDisplayName
{
220 let crate_name
= CrateName
::normalize_dashes(&canonical_name
);
221 CrateDisplayName { crate_name, canonical_name }
225 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
226 pub struct ProcMacroId(pub u32);
228 #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
229 pub enum ProcMacroKind
{
235 pub trait ProcMacroExpander
: fmt
::Debug
+ Send
+ Sync
+ RefUnwindSafe
{
239 attrs
: Option
<&Subtree
>,
241 ) -> Result
<Subtree
, ProcMacroExpansionError
>;
244 pub enum ProcMacroExpansionError
{
246 /// Things like "proc macro server was killed by OOM".
250 pub type ProcMacroLoadResult
= Result
<Vec
<ProcMacro
>, String
>;
252 #[derive(Debug, Clone)]
253 pub struct ProcMacro
{
255 pub kind
: ProcMacroKind
,
256 pub expander
: Arc
<dyn ProcMacroExpander
>,
259 #[derive(Debug, Clone)]
260 pub struct CrateData
{
261 pub root_file_id
: FileId
,
262 pub edition
: Edition
,
263 pub version
: Option
<String
>,
264 /// A name used in the package's project declaration: for Cargo projects,
265 /// its `[package].name` can be different for other project types or even
266 /// absent (a dummy crate for the code snippet, for example).
268 /// For purposes of analysis, crates are anonymous (only names in
269 /// `Dependency` matters), this name should only be used for UI.
270 pub display_name
: Option
<CrateDisplayName
>,
271 pub cfg_options
: CfgOptions
,
272 pub potential_cfg_options
: CfgOptions
,
274 pub dependencies
: Vec
<Dependency
>,
275 pub proc_macro
: ProcMacroLoadResult
,
276 pub origin
: CrateOrigin
,
277 pub is_proc_macro
: bool
,
280 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
288 pub const CURRENT
: Edition
= Edition
::Edition2018
;
291 #[derive(Default, Debug, Clone, PartialEq, Eq)]
293 entries
: FxHashMap
<String
, String
>,
296 #[derive(Debug, Clone, PartialEq, Eq)]
297 pub struct Dependency
{
298 pub crate_id
: CrateId
,
304 pub fn new(name
: CrateName
, crate_id
: CrateId
) -> Self {
305 Self { name, crate_id, prelude: true }
308 pub fn with_prelude(name
: CrateName
, crate_id
: CrateId
, prelude
: bool
) -> Self {
309 Self { name, crate_id, prelude }
312 /// Whether this dependency is to be added to the depending crate's extern prelude.
313 pub fn is_prelude(&self) -> bool
{
319 pub fn add_crate_root(
321 root_file_id
: FileId
,
323 display_name
: Option
<CrateDisplayName
>,
324 version
: Option
<String
>,
325 cfg_options
: CfgOptions
,
326 potential_cfg_options
: CfgOptions
,
328 proc_macro
: ProcMacroLoadResult
,
332 let data
= CrateData
{
338 potential_cfg_options
,
341 dependencies
: Vec
::new(),
345 let crate_id
= CrateId(self.arena
.len() as u32);
346 let prev
= self.arena
.insert(crate_id
, data
);
347 assert
!(prev
.is_none());
355 ) -> Result
<(), CyclicDependenciesError
> {
356 let _p
= profile
::span("add_dep");
358 // Check if adding a dep from `from` to `to` creates a cycle. To figure
359 // that out, look for a path in the *opposite* direction, from `to` to
361 if let Some(path
) = self.find_path(&mut NoHashHashSet
::default(), dep
.crate_id
, from
) {
362 let path
= path
.into_iter().map(|it
| (it
, self[it
].display_name
.clone())).collect();
363 let err
= CyclicDependenciesError { path }
;
364 assert
!(err
.from().0 == from
&& err
.to().0 == dep
.crate_id
);
368 self.arena
.get_mut(&from
).unwrap().add_dep(dep
);
372 pub fn is_empty(&self) -> bool
{
373 self.arena
.is_empty()
376 pub fn iter(&self) -> impl Iterator
<Item
= CrateId
> + '_
{
377 self.arena
.keys().copied()
380 /// Returns an iterator over all transitive dependencies of the given crate,
381 /// including the crate itself.
382 pub fn transitive_deps(&self, of
: CrateId
) -> impl Iterator
<Item
= CrateId
> {
383 let mut worklist
= vec
![of
];
384 let mut deps
= NoHashHashSet
::default();
386 while let Some(krate
) = worklist
.pop() {
387 if !deps
.insert(krate
) {
391 worklist
.extend(self[krate
].dependencies
.iter().map(|dep
| dep
.crate_id
));
397 /// Returns all transitive reverse dependencies of the given crate,
398 /// including the crate itself.
399 pub fn transitive_rev_deps(&self, of
: CrateId
) -> impl Iterator
<Item
= CrateId
> {
400 let mut worklist
= vec
![of
];
401 let mut rev_deps
= NoHashHashSet
::default();
404 let mut inverted_graph
= NoHashHashMap
::<_
, Vec
<_
>>::default();
405 self.arena
.iter().for_each(|(&krate
, data
)| {
408 .for_each(|dep
| inverted_graph
.entry(dep
.crate_id
).or_default().push(krate
))
411 while let Some(krate
) = worklist
.pop() {
412 if let Some(krate_rev_deps
) = inverted_graph
.get(&krate
) {
416 .filter(|&rev_dep
| rev_deps
.insert(rev_dep
))
417 .for_each(|rev_dep
| worklist
.push(rev_dep
));
424 /// Returns all crates in the graph, sorted in topological order (ie. dependencies of a crate
425 /// come before the crate itself).
426 pub fn crates_in_topological_order(&self) -> Vec
<CrateId
> {
427 let mut res
= Vec
::new();
428 let mut visited
= NoHashHashSet
::default();
430 for krate
in self.arena
.keys().copied() {
431 go(self, &mut visited
, &mut res
, krate
);
438 visited
: &mut NoHashHashSet
<CrateId
>,
439 res
: &mut Vec
<CrateId
>,
442 if !visited
.insert(source
) {
445 for dep
in graph
[source
].dependencies
.iter() {
446 go(graph
, visited
, res
, dep
.crate_id
)
452 // FIXME: this only finds one crate with the given root; we could have multiple
453 pub fn crate_id_for_crate_root(&self, file_id
: FileId
) -> Option
<CrateId
> {
455 self.arena
.iter().find(|(_crate_id
, data
)| data
.root_file_id
== file_id
)?
;
459 /// Extends this crate graph by adding a complete disjoint second crate
462 /// The ids of the crates in the `other` graph are shifted by the return
464 pub fn extend(&mut self, other
: CrateGraph
) -> u32 {
465 let start
= self.arena
.len() as u32;
466 self.arena
.extend(other
.arena
.into_iter().map(|(id
, mut data
)| {
467 let new_id
= id
.shift(start
);
468 for dep
in &mut data
.dependencies
{
469 dep
.crate_id
= dep
.crate_id
.shift(start
);
478 visited
: &mut NoHashHashSet
<CrateId
>,
481 ) -> Option
<Vec
<CrateId
>> {
482 if !visited
.insert(from
) {
487 return Some(vec
![to
]);
490 for dep
in &self[from
].dependencies
{
491 let crate_id
= dep
.crate_id
;
492 if let Some(mut path
) = self.find_path(visited
, crate_id
, to
) {
501 // Work around for https://github.com/rust-lang/rust-analyzer/issues/6038.
502 // As hacky as it gets.
503 pub fn patch_cfg_if(&mut self) -> bool
{
504 let cfg_if
= self.hacky_find_crate("cfg_if");
505 let std
= self.hacky_find_crate("std");
506 match (cfg_if
, std
) {
507 (Some(cfg_if
), Some(std
)) => {
508 self.arena
.get_mut(&cfg_if
).unwrap().dependencies
.clear();
513 .push(Dependency
::new(CrateName
::new("cfg_if").unwrap(), cfg_if
));
520 fn hacky_find_crate(&self, display_name
: &str) -> Option
<CrateId
> {
521 self.iter().find(|it
| self[*it
].display_name
.as_deref() == Some(display_name
))
525 impl ops
::Index
<CrateId
> for CrateGraph
{
526 type Output
= CrateData
;
527 fn index(&self, crate_id
: CrateId
) -> &CrateData
{
528 &self.arena
[&crate_id
]
533 fn shift(self, amount
: u32) -> CrateId
{
534 CrateId(self.0 + amount
)
539 fn add_dep(&mut self, dep
: Dependency
) {
540 self.dependencies
.push(dep
)
544 impl FromStr
for Edition
{
545 type Err
= ParseEditionError
;
547 fn from_str(s
: &str) -> Result
<Self, Self::Err
> {
549 "2015" => Edition
::Edition2015
,
550 "2018" => Edition
::Edition2018
,
551 "2021" => Edition
::Edition2021
,
552 _
=> return Err(ParseEditionError { invalid_input: s.to_string() }
),
558 impl fmt
::Display
for Edition
{
559 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
560 f
.write_str(match self {
561 Edition
::Edition2015
=> "2015",
562 Edition
::Edition2018
=> "2018",
563 Edition
::Edition2021
=> "2021",
568 impl FromIterator
<(String
, String
)> for Env
{
569 fn from_iter
<T
: IntoIterator
<Item
= (String
, String
)>>(iter
: T
) -> Self {
570 Env { entries: FromIterator::from_iter(iter) }
575 pub fn set(&mut self, env
: &str, value
: String
) {
576 self.entries
.insert(env
.to_owned(), value
);
579 pub fn get(&self, env
: &str) -> Option
<String
> {
580 self.entries
.get(env
).cloned()
583 pub fn iter(&self) -> impl Iterator
<Item
= (&str, &str)> {
584 self.entries
.iter().map(|(k
, v
)| (k
.as_str(), v
.as_str()))
589 pub struct ParseEditionError
{
590 invalid_input
: String
,
593 impl fmt
::Display
for ParseEditionError
{
594 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
595 write
!(f
, "invalid edition: {:?}", self.invalid_input
)
599 impl std
::error
::Error
for ParseEditionError {}
602 pub struct CyclicDependenciesError
{
603 path
: Vec
<(CrateId
, Option
<CrateDisplayName
>)>,
606 impl CyclicDependenciesError
{
607 fn from(&self) -> &(CrateId
, Option
<CrateDisplayName
>) {
608 self.path
.first().unwrap()
610 fn to(&self) -> &(CrateId
, Option
<CrateDisplayName
>) {
611 self.path
.last().unwrap()
615 impl fmt
::Display
for CyclicDependenciesError
{
616 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
617 let render
= |(id
, name
): &(CrateId
, Option
<CrateDisplayName
>)| match name
{
618 Some(it
) => format
!("{}({:?})", it
, id
),
619 None
=> format
!("{:?}", id
),
621 let path
= self.path
.iter().rev().map(render
).collect
::<Vec
<String
>>().join(" -> ");
624 "cyclic deps: {} -> {}, alternative path: {}",
634 use crate::CrateOrigin
;
636 use super::{CfgOptions, CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId}
;
639 fn detect_cyclic_dependency_indirect() {
640 let mut graph
= CrateGraph
::default();
641 let crate1
= graph
.add_crate_root(
646 CfgOptions
::default(),
647 CfgOptions
::default(),
651 CrateOrigin
::CratesIo { repo: None }
,
653 let crate2
= graph
.add_crate_root(
658 CfgOptions
::default(),
659 CfgOptions
::default(),
663 CrateOrigin
::CratesIo { repo: None }
,
665 let crate3
= graph
.add_crate_root(
670 CfgOptions
::default(),
671 CfgOptions
::default(),
675 CrateOrigin
::CratesIo { repo: None }
,
678 .add_dep(crate1
, Dependency
::new(CrateName
::new("crate2").unwrap(), crate2
))
681 .add_dep(crate2
, Dependency
::new(CrateName
::new("crate3").unwrap(), crate3
))
684 .add_dep(crate3
, Dependency
::new(CrateName
::new("crate1").unwrap(), crate1
))
689 fn detect_cyclic_dependency_direct() {
690 let mut graph
= CrateGraph
::default();
691 let crate1
= graph
.add_crate_root(
696 CfgOptions
::default(),
697 CfgOptions
::default(),
701 CrateOrigin
::CratesIo { repo: None }
,
703 let crate2
= graph
.add_crate_root(
708 CfgOptions
::default(),
709 CfgOptions
::default(),
713 CrateOrigin
::CratesIo { repo: None }
,
716 .add_dep(crate1
, Dependency
::new(CrateName
::new("crate2").unwrap(), crate2
))
719 .add_dep(crate2
, Dependency
::new(CrateName
::new("crate2").unwrap(), crate2
))
725 let mut graph
= CrateGraph
::default();
726 let crate1
= graph
.add_crate_root(
731 CfgOptions
::default(),
732 CfgOptions
::default(),
736 CrateOrigin
::CratesIo { repo: None }
,
738 let crate2
= graph
.add_crate_root(
743 CfgOptions
::default(),
744 CfgOptions
::default(),
748 CrateOrigin
::CratesIo { repo: None }
,
750 let crate3
= graph
.add_crate_root(
755 CfgOptions
::default(),
756 CfgOptions
::default(),
760 CrateOrigin
::CratesIo { repo: None }
,
763 .add_dep(crate1
, Dependency
::new(CrateName
::new("crate2").unwrap(), crate2
))
766 .add_dep(crate2
, Dependency
::new(CrateName
::new("crate3").unwrap(), crate3
))
771 fn dashes_are_normalized() {
772 let mut graph
= CrateGraph
::default();
773 let crate1
= graph
.add_crate_root(
778 CfgOptions
::default(),
779 CfgOptions
::default(),
783 CrateOrigin
::CratesIo { repo: None }
,
785 let crate2
= graph
.add_crate_root(
790 CfgOptions
::default(),
791 CfgOptions
::default(),
795 CrateOrigin
::CratesIo { repo: None }
,
800 Dependency
::new(CrateName
::normalize_dashes("crate-name-with-dashes"), crate2
)
804 graph
[crate1
].dependencies
,
805 vec
![Dependency
::new(CrateName
::new("crate_name_with_dashes").unwrap(), crate2
)]