rls_analysis/
lib.rs

1#![warn(rust_2018_idioms)]
2
3#[macro_use]
4extern crate derive_new;
5#[macro_use]
6extern crate log;
7
8extern crate rls_data as data;
9extern crate rls_span as span;
10
11mod analysis;
12mod listings;
13mod loader;
14mod lowering;
15mod raw;
16mod symbol_query;
17#[cfg(test)]
18mod test;
19mod util;
20
21use analysis::Analysis;
22pub use analysis::{Def, Ident, IdentKind, Ref};
23pub use loader::{AnalysisLoader, CargoAnalysisLoader, SearchDirectory, Target};
24pub use raw::{
25    deserialize_crate_data, name_space_for_def_kind, read_analysis_from_files, read_crate_data,
26    Crate, CrateId, DefKind,
27};
28pub use symbol_query::SymbolQuery;
29
30use std::collections::HashMap;
31use std::fmt::Debug;
32use std::path::{Path, PathBuf};
33use std::sync::Mutex;
34use std::time::{Instant, SystemTime};
35use std::u64;
36
37pub struct AnalysisHost<L: AnalysisLoader = CargoAnalysisLoader> {
38    analysis: Mutex<Option<Analysis>>,
39    master_crate_map: Mutex<HashMap<CrateId, u32>>,
40    loader: Mutex<L>,
41}
42
43pub type AResult<T> = Result<T, AError>;
44
45#[derive(Debug, Copy, Clone, PartialEq, Eq)]
46pub enum AError {
47    MutexPoison,
48    Unclassified,
49}
50
51#[derive(Debug, Clone)]
52pub struct SymbolResult {
53    pub id: Id,
54    pub name: String,
55    pub kind: raw::DefKind,
56    pub span: Span,
57    pub parent: Option<Id>,
58}
59
60impl SymbolResult {
61    fn new(id: Id, def: &Def) -> SymbolResult {
62        SymbolResult {
63            id,
64            name: def.name.clone(),
65            span: def.span.clone(),
66            kind: def.kind,
67            parent: def.parent,
68        }
69    }
70}
71
72pub type Span = span::Span<span::ZeroIndexed>;
73
74/// A common identifier for definitions, references etc. This is effectively a
75/// `DefId` with globally unique crate number (instead of a compiler generated
76/// crate-local number).
77#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, new)]
78pub struct Id(u64);
79
80impl Id {
81    fn from_crate_and_local(crate_id: u32, local_id: u32) -> Id {
82        // Use global crate number for high order bits,
83        // then index for least significant bits.
84        Id((u64::from(crate_id) << 32) | u64::from(local_id))
85    }
86}
87
88/// Used to indicate a missing index in the Id.
89pub const NULL: Id = Id(u64::MAX);
90
91macro_rules! clone_field {
92    ($field: ident) => {
93        |x| x.$field.clone()
94    };
95}
96
97macro_rules! def_span {
98    ($analysis: expr, $id: expr) => {
99        $analysis.with_defs_and_then($id, |def| Some(def.span.clone()))
100    };
101}
102
103impl AnalysisHost<CargoAnalysisLoader> {
104    pub fn new(target: Target) -> AnalysisHost {
105        AnalysisHost {
106            analysis: Mutex::new(None),
107            master_crate_map: Mutex::new(HashMap::new()),
108            loader: Mutex::new(CargoAnalysisLoader::new(target)),
109        }
110    }
111}
112
113impl<L: AnalysisLoader> AnalysisHost<L> {
114    pub fn new_with_loader(loader: L) -> Self {
115        Self {
116            analysis: Mutex::new(None),
117            master_crate_map: Mutex::new(HashMap::new()),
118            loader: Mutex::new(loader),
119        }
120    }
121
122    /// Reloads given data passed in `analysis`. This will first check and read
123    /// on-disk data (just like `reload`). It then imports the data we're
124    /// passing in directly.
125    pub fn reload_from_analysis(
126        &self,
127        analysis: Vec<data::Analysis>,
128        path_prefix: &Path,
129        base_dir: &Path,
130        blacklist: &[impl AsRef<str> + Debug],
131    ) -> AResult<()> {
132        self.reload_with_blacklist(path_prefix, base_dir, blacklist)?;
133
134        let crates: Vec<_> = analysis
135            .into_iter()
136            .map(|analysis| raw::Crate::new(analysis, SystemTime::now(), None, None))
137            .collect();
138
139        lowering::lower(crates, base_dir, self, |host, per_crate, id| {
140            let mut a = host.analysis.lock()?;
141            a.as_mut().unwrap().update(id, per_crate);
142            Ok(())
143        })
144    }
145
146    pub fn reload(&self, path_prefix: &Path, base_dir: &Path) -> AResult<()> {
147        self.reload_with_blacklist(path_prefix, base_dir, &[] as &[&str])
148    }
149
150    pub fn reload_with_blacklist(
151        &self,
152        path_prefix: &Path,
153        base_dir: &Path,
154        blacklist: &[impl AsRef<str> + Debug],
155    ) -> AResult<()> {
156        trace!("reload_with_blacklist {:?} {:?} {:?}", path_prefix, base_dir, blacklist);
157        let empty = self.analysis.lock()?.is_none();
158        if empty || self.loader.lock()?.needs_hard_reload(path_prefix) {
159            return self.hard_reload_with_blacklist(path_prefix, base_dir, blacklist);
160        }
161
162        let timestamps = self.analysis.lock()?.as_ref().unwrap().timestamps();
163        let raw_analysis = {
164            let loader = self.loader.lock()?;
165            read_analysis_from_files(&*loader, timestamps, blacklist)
166        };
167
168        lowering::lower(raw_analysis, base_dir, self, |host, per_crate, id| {
169            let mut a = host.analysis.lock()?;
170            a.as_mut().unwrap().update(id, per_crate);
171            Ok(())
172        })
173    }
174
175    /// Reloads the entire project's analysis data.
176    pub fn hard_reload(&self, path_prefix: &Path, base_dir: &Path) -> AResult<()> {
177        self.hard_reload_with_blacklist(path_prefix, base_dir, &[] as &[&str])
178    }
179
180    pub fn hard_reload_with_blacklist(
181        &self,
182        path_prefix: &Path,
183        base_dir: &Path,
184        blacklist: &[impl AsRef<str> + Debug],
185    ) -> AResult<()> {
186        trace!("hard_reload {:?} {:?}", path_prefix, base_dir);
187        // We're going to create a dummy AnalysisHost that we will fill with data,
188        // then once we're done, we'll swap its data into self.
189        let mut fresh_host = self.loader.lock()?.fresh_host();
190        fresh_host.analysis = Mutex::new(Some(Analysis::new()));
191
192        {
193            let mut fresh_loader = fresh_host.loader.lock().unwrap();
194            fresh_loader.set_path_prefix(path_prefix); // TODO: Needed?
195
196            let raw_analysis = read_analysis_from_files(&*fresh_loader, HashMap::new(), blacklist);
197            lowering::lower(raw_analysis, base_dir, &fresh_host, |host, per_crate, id| {
198                let mut a = host.analysis.lock()?;
199                a.as_mut().unwrap().update(id, per_crate);
200                Ok(())
201            })?;
202        }
203
204        // To guarantee a consistent state and no corruption in case an error
205        // happens during reloading, we need to swap data with a dummy host in
206        // a single atomic step. We can't lock and swap every member at a time,
207        // as this can possibly lead to inconsistent state, but now this can possibly
208        // deadlock, which isn't that good. Ideally we should have guaranteed
209        // exclusive access to AnalysisHost as a whole to perform a reliable swap.
210        macro_rules! swap_mutex_fields {
211            ($($name:ident),*) => {
212                // First, we need exclusive access to every field before swapping
213                $(let mut $name = self.$name.lock()?;)*
214                // Then, we can swap every field
215                $(*$name = fresh_host.$name.into_inner().unwrap();)*
216            };
217        }
218
219        swap_mutex_fields!(analysis, master_crate_map, loader);
220
221        Ok(())
222    }
223
224    /// Note that `self.has_def()` =/> `self.goto_def().is_ok()`, since if the
225    /// Def is in an api crate, there is no reasonable Span to jump to.
226    pub fn has_def(&self, id: Id) -> bool {
227        match self.analysis.lock() {
228            Ok(a) => a.as_ref().unwrap().has_def(id),
229            _ => false,
230        }
231    }
232
233    pub fn get_def(&self, id: Id) -> AResult<Def> {
234        self.with_analysis(|a| a.with_defs(id, Clone::clone))
235    }
236
237    pub fn goto_def(&self, span: &Span) -> AResult<Span> {
238        self.with_analysis(|a| a.def_id_for_span(span).and_then(|id| def_span!(a, id)))
239    }
240
241    pub fn for_each_child_def<F, T>(&self, id: Id, f: F) -> AResult<Vec<T>>
242    where
243        F: FnMut(Id, &Def) -> T,
244    {
245        self.with_analysis(|a| a.for_each_child(id, f))
246    }
247
248    pub fn def_parents(&self, id: Id) -> AResult<Vec<(Id, String)>> {
249        self.with_analysis(|a| {
250            let mut result = vec![];
251            let mut next = id;
252            loop {
253                match a.with_defs_and_then(next, |def| {
254                    def.parent.and_then(|p| a.with_defs(p, |def| (p, def.name.clone())))
255                }) {
256                    Some((id, name)) => {
257                        result.insert(0, (id, name));
258                        next = id;
259                    }
260                    None => {
261                        return Some(result);
262                    }
263                }
264            }
265        })
266    }
267
268    /// Returns the name of each crate in the program and the id of the root
269    /// module of that crate.
270    pub fn def_roots(&self) -> AResult<Vec<(Id, String)>> {
271        self.with_analysis(|a| {
272            Some(
273                a.per_crate
274                    .iter()
275                    .filter_map(|(crate_id, data)| {
276                        data.root_id.map(|id| (id, crate_id.name.clone()))
277                    })
278                    .collect(),
279            )
280        })
281    }
282
283    pub fn id(&self, span: &Span) -> AResult<Id> {
284        self.with_analysis(|a| a.def_id_for_span(span))
285    }
286
287    /// Like id, but will only return a value if it is in the same crate as span.
288    pub fn crate_local_id(&self, span: &Span) -> AResult<Id> {
289        self.with_analysis(|a| a.local_def_id_for_span(span))
290    }
291
292    // `include_decl` means the declaration will be included as the first result.
293    // `force_unique_spans` means that if any reference is a reference to multiple
294    // defs, then we return an empty vector (in which case, even if include_decl
295    // is true, the result will be empty).
296    // Note that for large numbers of refs, if `force_unique_spans` is true, then
297    // this function might take significantly longer to execute.
298    pub fn find_all_refs(
299        &self,
300        span: &Span,
301        include_decl: bool,
302        force_unique_spans: bool,
303    ) -> AResult<Vec<Span>> {
304        let t_start = Instant::now();
305        let result = self.with_analysis(|a| {
306            a.def_id_for_span(span).map(|id| {
307                if force_unique_spans && a.aliased_imports.contains(&id) {
308                    return vec![];
309                }
310                let decl = if include_decl { def_span!(a, id) } else { None };
311                let refs = a.with_ref_spans(id, |refs| {
312                    if force_unique_spans {
313                        for r in refs.iter() {
314                            match a.ref_for_span(r) {
315                                Some(Ref::Id(_)) => {}
316                                _ => return None,
317                            }
318                        }
319                    }
320                    Some(refs.clone())
321                });
322                refs.map(|refs| decl.into_iter().chain(refs.into_iter()).collect::<Vec<_>>())
323                    .unwrap_or_else(|| vec![])
324            })
325        });
326
327        let time = t_start.elapsed();
328        info!(
329            "find_all_refs: {}s",
330            time.as_secs() as f64 + f64::from(time.subsec_nanos()) / 1_000_000_000.0
331        );
332        result
333    }
334
335    pub fn show_type(&self, span: &Span) -> AResult<String> {
336        self.with_analysis(|a| {
337            a.def_id_for_span(span)
338                .and_then(|id| a.with_defs(id, clone_field!(value)))
339                .or_else(|| a.with_globs(span, clone_field!(value)))
340        })
341    }
342
343    pub fn docs(&self, span: &Span) -> AResult<String> {
344        self.with_analysis(|a| {
345            a.def_id_for_span(span).and_then(|id| a.with_defs(id, clone_field!(docs)))
346        })
347    }
348
349    /// Finds Defs with names that starting with (ignoring case) `stem`
350    pub fn matching_defs(&self, stem: &str) -> AResult<Vec<Def>> {
351        self.query_defs(SymbolQuery::prefix(stem))
352    }
353
354    pub fn query_defs(&self, query: SymbolQuery) -> AResult<Vec<Def>> {
355        let t_start = Instant::now();
356        let result = self.with_analysis(move |a| {
357            let defs = a.query_defs(query);
358            info!("query_defs {:?}", &defs);
359            Some(defs)
360        });
361
362        let time = t_start.elapsed();
363        info!(
364            "query_defs: {}",
365            time.as_secs() as f64 + f64::from(time.subsec_nanos()) / 1_000_000_000.0
366        );
367
368        result
369    }
370
371    /// Search for a symbol name, returns a list of spans matching defs and refs
372    /// for that name.
373    pub fn search(&self, name: &str) -> AResult<Vec<Span>> {
374        let t_start = Instant::now();
375        let result = self.with_analysis(|a| {
376            Some(a.with_def_names(name, |defs| {
377                info!("defs: {:?}", defs);
378                defs.iter()
379                    .flat_map(|id| {
380                        a.with_ref_spans(*id, |refs| {
381                            Some(
382                                def_span!(a, *id)
383                                    .into_iter()
384                                    .chain(refs.iter().cloned())
385                                    .collect::<Vec<_>>(),
386                            )
387                        })
388                        .or_else(|| def_span!(a, *id).map(|s| vec![s]))
389                        .unwrap_or_else(Vec::new)
390                        .into_iter()
391                    })
392                    .collect::<Vec<Span>>()
393            }))
394        });
395
396        let time = t_start.elapsed();
397        info!(
398            "search: {}s",
399            time.as_secs() as f64 + f64::from(time.subsec_nanos()) / 1_000_000_000.0
400        );
401        result
402    }
403
404    // TODO refactor search and find_all_refs to use this
405    // Includes all references and the def, the def is always first.
406    pub fn find_all_refs_by_id(&self, id: Id) -> AResult<Vec<Span>> {
407        let t_start = Instant::now();
408        let result = self.with_analysis(|a| {
409            a.with_ref_spans(id, |refs| {
410                Some(def_span!(a, id).into_iter().chain(refs.iter().cloned()).collect::<Vec<_>>())
411            })
412            .or_else(|| def_span!(a, id).map(|s| vec![s]))
413        });
414
415        let time = t_start.elapsed();
416        info!(
417            "find_all_refs_by_id: {}s",
418            time.as_secs() as f64 + f64::from(time.subsec_nanos()) / 1_000_000_000.0
419        );
420        result
421    }
422
423    pub fn find_impls(&self, id: Id) -> AResult<Vec<Span>> {
424        self.with_analysis(|a| Some(a.for_all_crates(|c| c.impls.get(&id).cloned())))
425    }
426
427    /// Search for a symbol name, returning a list of def_ids for that name.
428    pub fn search_for_id(&self, name: &str) -> AResult<Vec<Id>> {
429        self.with_analysis(|a| Some(a.with_def_names(name, Clone::clone)))
430    }
431
432    /// Returns all identifiers which overlap the given span.
433    #[cfg(feature = "idents")]
434    pub fn idents(&self, span: &Span) -> AResult<Vec<Ident>> {
435        self.with_analysis(|a| Some(a.idents(span)))
436    }
437
438    pub fn symbols(&self, file_name: &Path) -> AResult<Vec<SymbolResult>> {
439        self.with_analysis(|a| {
440            a.with_defs_per_file(file_name, |ids| {
441                ids.iter()
442                    .map(|id| a.with_defs(*id, |def| SymbolResult::new(*id, def)).unwrap())
443                    .collect()
444            })
445        })
446    }
447
448    pub fn doc_url(&self, span: &Span) -> AResult<String> {
449        // e.g., https://doc.rust-lang.org/nightly/std/string/String.t.html
450        self.with_analysis(|a| {
451            a.def_id_for_span(span).and_then(|id| {
452                a.with_defs_and_then(id, |def| AnalysisHost::<L>::mk_doc_url(def, a))
453            })
454        })
455    }
456
457    // e.g., https://github.com/rust-lang/rust/blob/master/src/liballoc/string.rs#L261-L263
458    pub fn src_url(&self, span: &Span) -> AResult<String> {
459        // FIXME would be nice not to do this every time.
460        let path_prefix = self.loader.lock().unwrap().abs_path_prefix();
461
462        self.with_analysis(|a| {
463            a.def_id_for_span(span).and_then(|id| {
464                a.with_defs_and_then(id, |def| {
465                    AnalysisHost::<L>::mk_src_url(def, path_prefix.as_ref(), a)
466                })
467            })
468        })
469    }
470
471    fn with_analysis<F, T>(&self, f: F) -> AResult<T>
472    where
473        F: FnOnce(&Analysis) -> Option<T>,
474    {
475        let a = self.analysis.lock()?;
476        if let Some(ref a) = *a {
477            f(a).ok_or(AError::Unclassified)
478        } else {
479            Err(AError::Unclassified)
480        }
481    }
482
483    fn mk_doc_url(def: &Def, analysis: &Analysis) -> Option<String> {
484        if !def.distro_crate {
485            return None;
486        }
487
488        if def.parent.is_none() && def.qualname.contains('<') {
489            debug!("mk_doc_url, bailing, found generic qualname: `{}`", def.qualname);
490            return None;
491        }
492
493        match def.parent {
494            Some(p) => analysis.with_defs(p, |parent| match def.kind {
495                DefKind::Field
496                | DefKind::Method
497                | DefKind::Tuple
498                | DefKind::TupleVariant
499                | DefKind::StructVariant => {
500                    let ns = name_space_for_def_kind(def.kind);
501                    let mut res = AnalysisHost::<L>::mk_doc_url(parent, analysis)
502                        .unwrap_or_else(|| "".into());
503                    res.push_str(&format!("#{}.{}", def.name, ns));
504                    res
505                }
506                DefKind::Mod => {
507                    let parent_qualpath = parent.qualname.replace("::", "/");
508                    format!(
509                        "{}/{}/{}/",
510                        analysis.doc_url_base,
511                        parent_qualpath.trim_end_matches('/'),
512                        def.name,
513                    )
514                }
515                _ => {
516                    let parent_qualpath = parent.qualname.replace("::", "/");
517                    let ns = name_space_for_def_kind(def.kind);
518                    format!(
519                        "{}/{}/{}.{}.html",
520                        analysis.doc_url_base, parent_qualpath, def.name, ns,
521                    )
522                }
523            }),
524            None => {
525                let qualpath = def.qualname.replace("::", "/");
526                let ns = name_space_for_def_kind(def.kind);
527                Some(format!("{}/{}.{}.html", analysis.doc_url_base, qualpath, ns,))
528            }
529        }
530    }
531
532    fn mk_src_url(def: &Def, path_prefix: Option<&PathBuf>, analysis: &Analysis) -> Option<String> {
533        if !def.distro_crate {
534            return None;
535        }
536
537        let file_path = &def.span.file;
538        let file_path = file_path.strip_prefix(path_prefix?).ok()?;
539
540        Some(format!(
541            "{}/{}#L{}-L{}",
542            analysis.src_url_base,
543            file_path.to_str().unwrap(),
544            def.span.range.row_start.one_indexed().0,
545            def.span.range.row_end.one_indexed().0
546        ))
547    }
548}
549
550impl ::std::fmt::Display for Id {
551    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
552        ::std::fmt::Display::fmt(&self.0, f)
553    }
554}
555
556impl ::std::error::Error for AError {}
557
558impl ::std::fmt::Display for AError {
559    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
560        let description = match self {
561            AError::MutexPoison => "poison error in a mutex (usually a secondary error)",
562            AError::Unclassified => "unknown error",
563        };
564        write!(f, "{}", description)
565    }
566}
567
568impl<T> From<::std::sync::PoisonError<T>> for AError {
569    fn from(_: ::std::sync::PoisonError<T>) -> AError {
570        AError::MutexPoison
571    }
572}