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, iter::FromIterator, ops, str::FromStr, sync::Arc};
12 use rustc_hash::{FxHashMap, FxHashSet};
14 use tt::TokenExpander;
15 use vfs::file_set::FileSet;
19 /// Files are grouped into source roots. A source root is a directory on the
20 /// file systems which is watched for changes. Typically it corresponds to a
21 /// Rust crate. Source roots *might* be nested: in this case, a file belongs to
22 /// the nearest enclosing source root. Paths to files are always relative to a
23 /// source root, and the analyzer does not know the root path of the source root at
24 /// all. So, a file from one source root can't refer to a file in another source
26 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
27 pub struct SourceRootId(pub u32);
29 #[derive(Clone, Debug, PartialEq, Eq)]
30 pub struct SourceRoot {
31 /// Sysroot or crates.io library.
33 /// Libraries are considered mostly immutable, this assumption is used to
34 /// optimize salsa's query structure
36 pub(crate) file_set: FileSet,
40 pub fn new_local(file_set: FileSet) -> SourceRoot {
41 SourceRoot { is_library: false, file_set }
43 pub fn new_library(file_set: FileSet) -> SourceRoot {
44 SourceRoot { is_library: true, file_set }
46 pub fn iter(&self) -> impl Iterator<Item = FileId> + '_ {
51 /// `CrateGraph` is a bit of information which turns a set of text files into a
52 /// number of Rust crates. Each crate is defined by the `FileId` of its root module,
53 /// the set of cfg flags (not yet implemented) and the set of dependencies. Note
54 /// that, due to cfg's, there might be several crates for a single `FileId`! As
55 /// in the rust-lang proper, a crate does not have a name. Instead, names are
56 /// specified on dependency edges. That is, a crate might be known under
57 /// different names in different dependent crates.
59 /// Note that `CrateGraph` is build-system agnostic: it's a concept of the Rust
60 /// language proper, not a concept of the build system. In practice, we get
61 /// `CrateGraph` by lowering `cargo metadata` output.
62 #[derive(Debug, Clone, Default, PartialEq, Eq)]
63 pub struct CrateGraph {
64 arena: FxHashMap<CrateId, CrateData>,
67 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
68 pub struct CrateId(pub u32);
70 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
71 pub struct CrateName(SmolStr);
74 /// Creates a crate name, checking for dashes in the string provided.
75 /// Dashes are not allowed in the crate names,
76 /// hence the input string is returned as `Err` for those cases.
77 pub fn new(name: &str) -> Result<CrateName, &str> {
78 if name.contains('-') {
81 Ok(Self(SmolStr::new(name)))
85 /// Creates a crate name, unconditionally replacing the dashes with underscores.
86 pub fn normalize_dashes(name: &str) -> CrateName {
87 Self(SmolStr::new(name.replace('-', "_")))
91 impl fmt::Display for CrateName {
92 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93 write!(f, "{}", self.0)
97 impl ops::Deref for CrateName {
99 fn deref(&self) -> &Self::Target {
104 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
105 pub struct ProcMacroId(pub u32);
107 #[derive(Debug, Clone)]
108 pub struct ProcMacro {
110 pub expander: Arc<dyn TokenExpander>,
113 impl Eq for ProcMacro {}
114 impl PartialEq for ProcMacro {
115 fn eq(&self, other: &ProcMacro) -> bool {
116 self.name == other.name && Arc::ptr_eq(&self.expander, &other.expander)
120 #[derive(Debug, Clone, PartialEq, Eq)]
121 pub struct CrateData {
122 pub root_file_id: FileId,
123 pub edition: Edition,
124 /// The name to display to the end user.
125 /// This actual crate name can be different in a particular dependent crate
126 /// or may even be missing for some cases, such as a dummy crate for the code snippet.
127 pub display_name: Option<String>,
128 pub cfg_options: CfgOptions,
130 pub dependencies: Vec<Dependency>,
131 pub proc_macro: Vec<ProcMacro>,
134 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
140 #[derive(Default, Debug, Clone, PartialEq, Eq)]
142 entries: FxHashMap<String, String>,
145 #[derive(Debug, Clone, PartialEq, Eq)]
146 pub struct Dependency {
147 pub crate_id: CrateId,
152 pub fn add_crate_root(
156 display_name: Option<String>,
157 cfg_options: CfgOptions,
159 proc_macro: Vec<(SmolStr, Arc<dyn tt::TokenExpander>)>,
162 proc_macro.into_iter().map(|(name, it)| ProcMacro { name, expander: it }).collect();
164 let data = CrateData {
165 root_file_id: file_id,
171 dependencies: Vec::new(),
173 let crate_id = CrateId(self.arena.len() as u32);
174 let prev = self.arena.insert(crate_id, data);
175 assert!(prev.is_none());
184 ) -> Result<(), CyclicDependenciesError> {
185 if self.dfs_find(from, to, &mut FxHashSet::default()) {
186 return Err(CyclicDependenciesError);
188 self.arena.get_mut(&from).unwrap().add_dep(name, to);
192 pub fn is_empty(&self) -> bool {
193 self.arena.is_empty()
196 pub fn iter(&self) -> impl Iterator<Item = CrateId> + '_ {
197 self.arena.keys().copied()
200 /// Returns an iterator over all transitive dependencies of the given crate.
201 pub fn transitive_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId> + '_ {
202 let mut worklist = vec![of];
203 let mut deps = FxHashSet::default();
205 while let Some(krate) = worklist.pop() {
206 if !deps.insert(krate) {
210 worklist.extend(self[krate].dependencies.iter().map(|dep| dep.crate_id));
217 // FIXME: this only finds one crate with the given root; we could have multiple
218 pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> {
220 self.arena.iter().find(|(_crate_id, data)| data.root_file_id == file_id)?;
224 /// Extends this crate graph by adding a complete disjoint second crate
227 /// The ids of the crates in the `other` graph are shifted by the return
229 pub fn extend(&mut self, other: CrateGraph) -> u32 {
230 let start = self.arena.len() as u32;
231 self.arena.extend(other.arena.into_iter().map(|(id, mut data)| {
232 let new_id = id.shift(start);
233 for dep in &mut data.dependencies {
234 dep.crate_id = dep.crate_id.shift(start);
241 fn dfs_find(&self, target: CrateId, from: CrateId, visited: &mut FxHashSet<CrateId>) -> bool {
242 if !visited.insert(from) {
250 for dep in &self[from].dependencies {
251 let crate_id = dep.crate_id;
252 if self.dfs_find(target, crate_id, visited) {
260 impl ops::Index<CrateId> for CrateGraph {
261 type Output = CrateData;
262 fn index(&self, crate_id: CrateId) -> &CrateData {
263 &self.arena[&crate_id]
268 pub fn shift(self, amount: u32) -> CrateId {
269 CrateId(self.0 + amount)
274 fn add_dep(&mut self, name: CrateName, crate_id: CrateId) {
275 self.dependencies.push(Dependency { name, crate_id })
279 impl FromStr for Edition {
280 type Err = ParseEditionError;
282 fn from_str(s: &str) -> Result<Self, Self::Err> {
284 "2015" => Edition::Edition2015,
285 "2018" => Edition::Edition2018,
286 _ => return Err(ParseEditionError { invalid_input: s.to_string() }),
292 impl fmt::Display for Edition {
293 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
294 f.write_str(match self {
295 Edition::Edition2015 => "2015",
296 Edition::Edition2018 => "2018",
301 impl FromIterator<(String, String)> for Env {
302 fn from_iter<T: IntoIterator<Item = (String, String)>>(iter: T) -> Self {
303 Env { entries: FromIterator::from_iter(iter) }
308 pub fn set(&mut self, env: &str, value: String) {
309 self.entries.insert(env.to_owned(), value);
312 pub fn get(&self, env: &str) -> Option<String> {
313 self.entries.get(env).cloned()
318 pub struct ParseEditionError {
319 invalid_input: String,
322 impl fmt::Display for ParseEditionError {
323 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
324 write!(f, "invalid edition: {:?}", self.invalid_input)
328 impl std::error::Error for ParseEditionError {}
331 pub struct CyclicDependenciesError;
335 use super::{CfgOptions, CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId};
338 fn detect_cyclic_dependency_indirect() {
339 let mut graph = CrateGraph::default();
340 let crate1 = graph.add_crate_root(
344 CfgOptions::default(),
348 let crate2 = graph.add_crate_root(
352 CfgOptions::default(),
356 let crate3 = graph.add_crate_root(
360 CfgOptions::default(),
364 assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok());
365 assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok());
366 assert!(graph.add_dep(crate3, CrateName::new("crate1").unwrap(), crate1).is_err());
370 fn detect_cyclic_dependency_direct() {
371 let mut graph = CrateGraph::default();
372 let crate1 = graph.add_crate_root(
376 CfgOptions::default(),
380 let crate2 = graph.add_crate_root(
384 CfgOptions::default(),
388 assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok());
389 assert!(graph.add_dep(crate2, CrateName::new("crate2").unwrap(), crate2).is_err());
394 let mut graph = CrateGraph::default();
395 let crate1 = graph.add_crate_root(
399 CfgOptions::default(),
403 let crate2 = graph.add_crate_root(
407 CfgOptions::default(),
411 let crate3 = graph.add_crate_root(
415 CfgOptions::default(),
419 assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok());
420 assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok());
424 fn dashes_are_normalized() {
425 let mut graph = CrateGraph::default();
426 let crate1 = graph.add_crate_root(
430 CfgOptions::default(),
434 let crate2 = graph.add_crate_root(
438 CfgOptions::default(),
443 .add_dep(crate1, CrateName::normalize_dashes("crate-name-with-dashes"), crate2)
446 graph[crate1].dependencies,
449 name: CrateName::new("crate_name_with_dashes").unwrap()