1 use crate::ast_types::{GenericsArgs, ImplHeader, Pat, TraitBounds, Ty, TypeParameter};
2 use crate::codecleaner;
3 use crate::codeiter::StmtIndicesIter;
4 use crate::matchers::ImportInfo;
5 use crate::project_model::ProjectModelProvider;
6 use rls_span;
7 use std::cell::RefCell;
8 use std::cmp::Ordering;
9 use std::collections::HashMap;
10 use std::fs::File;
11 use std::io;
12 use std::io::Read;
13 use std::iter::{Fuse, Iterator};
14 use std::ops::{Deref, Range};
15 use std::rc::Rc;
16 use std::{fmt, vec};
17 use std::{path, str};
18 use rustc_span::source_map;
19 
20 use crate::ast;
21 use crate::fileres;
22 use crate::nameres;
23 use crate::primitive::PrimKind;
24 use crate::scopes;
25 use crate::util;
26 
27 /// Within a [`Match`], specifies what was matched
28 ///
29 /// [`Match`]: struct.Match.html
30 #[derive(Clone, Debug, PartialEq)]
31 pub enum MatchType {
32     Struct(Box<GenericsArgs>),
33     Module,
34     MatchArm,
35     Function,
36     Method(Option<Box<GenericsArgs>>),
37     Crate,
38     Let(BytePos),
39     IfLet(BytePos),
40     WhileLet(BytePos),
41     For(BytePos),
42     StructField,
43     Enum(Box<GenericsArgs>),
44     Union(Box<GenericsArgs>),
45     /// EnumVariant needs to have Enum type to complete methods
46     EnumVariant(Option<Box<Match>>),
47     UseAlias(Box<Match>),
48     AssocType,
49     Type,
50     FnArg(Box<(Pat, Option<Ty>)>),
51     Trait,
52     Const,
53     Static,
54     Macro,
55     Builtin(PrimKind),
56     /// fn f<T: Clone> or fn f(a: impl Clone) with its trait bounds
57     TypeParameter(Box<TraitBounds>),
58 }
59 
60 impl MatchType {
is_function(&self) -> bool61     pub fn is_function(&self) -> bool {
62         match self {
63             MatchType::Function | MatchType::Method(_) => true,
64             _ => false,
65         }
66     }
is_enum(&self) -> bool67     pub fn is_enum(&self) -> bool {
68         match self {
69             MatchType::Enum(_) => true,
70             _ => false,
71         }
72     }
is_struct(&self) -> bool73     pub fn is_struct(&self) -> bool {
74         match self {
75             MatchType::Struct(_) => true,
76             _ => false,
77         }
78     }
79 }
80 
81 impl fmt::Display for MatchType {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result82     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83         match self {
84             MatchType::Struct(_) => write!(f, "Struct"),
85             MatchType::Union(_) => write!(f, "Union"),
86             MatchType::Method(_) => write!(f, "Method"),
87             MatchType::IfLet(_) => write!(f, "IfLet"),
88             MatchType::Let(_) => write!(f, "Let"),
89             MatchType::WhileLet(_) => write!(f, "WhileLet"),
90             MatchType::For(_) => write!(f, "For"),
91             MatchType::Enum(_) => write!(f, "Enum"),
92             MatchType::EnumVariant(_) => write!(f, "EnumVariant"),
93             MatchType::TypeParameter(_) => write!(f, "TypeParameter"),
94             MatchType::FnArg(_) => write!(f, "FnArg"),
95             MatchType::Type => write!(f, "Type"),
96             MatchType::UseAlias(_) => write!(f, "UseAlias"),
97             _ => fmt::Debug::fmt(self, f),
98         }
99     }
100 }
101 
102 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
103 pub enum SearchType {
104     ExactMatch,
105     StartsWith,
106 }
107 
108 mod declare_namespace {
109     // (kngwyu) I reserved Crate, Mod or other names for future usage(like for #830)
110     // but, currently they're not used and... I'm not sure they're useful:)
111     #![allow(non_upper_case_globals, unused)]
112     bitflags! {
113         /// Type context
114         pub struct Namespace: u32 {
115             const Crate     = 0b0000000000001;
116             const Mod       = 0b0000000000010;
117             const Space     = 0b0000000000011;
118             const Enum      = 0b0000000000100;
119             const Struct    = 0b0000000001000;
120             const Union     = 0b0000000010000;
121             const Trait     = 0b0000000100000;
122             const TypeDef   = 0b0000001000000;
123             const HasField  = 0b0000001011100;
124             const Type      = 0b0000001111100;
125             const PathParen = 0b0000001111111;
126             const Const     = 0b0000010000000;
127             const Static    = 0b0000100000000;
128             const Func      = 0b0001000000000;
129             // for use_extern_macros
130             const Macro     = 0b0010000000000;
131             const Impl      = 0b0001110000000;
132             const PathChild = 0b0011110000000;
133             const Path      = 0b0011111111111;
134             const Primitive = 0b0100000000000;
135             const StdMacro  = 0b1000000000000;
136             const Global    = 0b1100000000000;
137         }
138     }
139 }
140 pub use self::declare_namespace::Namespace;
141 
142 #[derive(Debug, Clone, Copy)]
143 pub enum CompletionType {
144     Field,
145     Path,
146 }
147 
148 /// 0-based byte offset in a file.
149 #[derive(
150     Clone,
151     Copy,
152     Debug,
153     Default,
154     Eq,
155     PartialEq,
156     Ord,
157     PartialOrd,
158     Hash,
159     Index,
160     From,
161     Add,
162     Sub,
163     AddAssign,
164     SubAssign,
165 )]
166 pub struct BytePos(pub usize);
167 
168 impl From<u32> for BytePos {
from(u: u32) -> Self169     fn from(u: u32) -> Self {
170         BytePos(u as usize)
171     }
172 }
173 
174 impl BytePos {
175     pub const ZERO: BytePos = BytePos(0);
176     /// returns self - 1
decrement(&self) -> Self177     pub fn decrement(&self) -> Self {
178         BytePos(self.0 - 1)
179     }
checked_sub(&self, sub: impl Into<Self>) -> Option<Self>180     pub fn checked_sub(&self, sub: impl Into<Self>) -> Option<Self> {
181         self.0.checked_sub(sub.into().0).map(BytePos)
182     }
try_decrement(&self) -> Option<Self>183     pub fn try_decrement(&self) -> Option<Self> {
184         self.0.checked_sub(1).map(BytePos)
185     }
186     /// returns self + 1
increment(&self) -> Self187     pub fn increment(&self) -> Self {
188         BytePos(self.0 + 1)
189     }
190 }
191 
192 impl fmt::Display for BytePos {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result193     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194         write!(f, "{}", self.0)
195     }
196 }
197 
198 /// 0-based byte range in a file.
199 #[derive(Clone, Copy, Default, Eq, PartialEq, Hash)]
200 pub struct ByteRange {
201     /// start of byte position in codes(inclusive)
202     pub start: BytePos,
203     /// end of byte position in codes(exclusive)
204     pub end: BytePos,
205 }
206 
207 impl ByteRange {
208     /// returns new ByteRange from start and end
new<P: Into<BytePos>>(start: P, end: P) -> Self209     pub fn new<P: Into<BytePos>>(start: P, end: P) -> Self {
210         ByteRange {
211             start: start.into(),
212             end: end.into(),
213         }
214     }
215 
216     /// returns the length of the range
217     #[inline]
len(&self) -> usize218     pub fn len(&self) -> usize {
219         (self.end - self.start).0
220     }
221 
222     /// returns if the range contains `point` or not
223     #[inline]
contains(&self, point: BytePos) -> bool224     pub fn contains(&self, point: BytePos) -> bool {
225         self.start <= point && point < self.end
226     }
227 
228     /// returns if the range contains `point` (except its start point)
229     #[inline]
contains_exclusive(&self, point: BytePos) -> bool230     pub fn contains_exclusive(&self, point: BytePos) -> bool {
231         self.start < point && point < self.end
232     }
233 
234     /// returns the new range with which its start is `self.start + shift`,
235     /// its end is `self.end + shift`
236     #[inline]
shift<P: Into<BytePos>>(&self, shift: P) -> Self237     pub fn shift<P: Into<BytePos>>(&self, shift: P) -> Self {
238         let shift = shift.into();
239         ByteRange {
240             start: self.start + shift,
241             end: self.end + shift,
242         }
243     }
244 
245     /// convert the range to `std::ops::Range`
246     #[inline]
to_range(&self) -> Range<usize>247     pub fn to_range(&self) -> Range<usize> {
248         self.start.0..self.end.0
249     }
250 }
251 
252 impl PartialEq<BytePos> for ByteRange {
eq(&self, other: &BytePos) -> bool253     fn eq(&self, other: &BytePos) -> bool {
254         self.contains(*other)
255     }
256 }
257 
258 impl PartialOrd<BytePos> for ByteRange {
partial_cmp(&self, other: &BytePos) -> Option<Ordering>259     fn partial_cmp(&self, other: &BytePos) -> Option<Ordering> {
260         if *other < self.start {
261             Some(Ordering::Greater)
262         } else if *other >= self.end {
263             Some(Ordering::Less)
264         } else {
265             Some(Ordering::Equal)
266         }
267     }
268 }
269 
270 impl From<source_map::Span> for ByteRange {
from(span: source_map::Span) -> Self271     fn from(span: source_map::Span) -> Self {
272         let (lo, hi) = ast::destruct_span(span);
273         ByteRange::new(lo, hi)
274     }
275 }
276 
277 impl fmt::Debug for ByteRange {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result278     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279         write!(f, "ByteRange({}..{})", self.start.0, self.end.0)
280     }
281 }
282 
283 /// Row and Column position in a file
284 // for backward compatibility, we use 1-index row and 0-indexed column here
285 #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
286 pub struct Coordinate {
287     pub row: rls_span::Row<rls_span::OneIndexed>,
288     pub col: rls_span::Column<rls_span::ZeroIndexed>,
289 }
290 
291 impl Coordinate {
292     /// construct new Coordinate
new(row: u32, col: u32) -> Self293     pub fn new(row: u32, col: u32) -> Self {
294         Coordinate {
295             row: rls_span::Row::<rls_span::OneIndexed>::new_one_indexed(row),
296             col: rls_span::Column::<rls_span::ZeroIndexed>::new_zero_indexed(col),
297         }
298     }
299     /// start point of the file
start() -> Self300     pub fn start() -> Self {
301         Coordinate::new(1, 0)
302     }
303 }
304 
305 /// Context, source, and etc. for detected completion or definition
306 #[derive(Clone, PartialEq)]
307 pub struct Match {
308     pub matchstr: String,
309     pub filepath: path::PathBuf,
310     pub point: BytePos,
311     pub coords: Option<Coordinate>,
312     pub local: bool,
313     pub mtype: MatchType,
314     pub contextstr: String,
315     pub docs: String,
316 }
317 
318 impl Match {
319     /// Checks if two matches can be considered the same for deduplication purposes.
320     ///
321     /// This could be the basis for a `PartialEq` implementation in the future,
322     /// but in the interest of minimizing the crate's public API surface it's exposed
323     /// as a private method for now.
is_same_as(&self, other: &Match) -> bool324     fn is_same_as(&self, other: &Match) -> bool {
325         self.point == other.point
326             && self.matchstr == other.matchstr
327             && self.filepath == other.filepath
328     }
to_generics(&self) -> Option<&GenericsArgs>329     pub(crate) fn to_generics(&self) -> Option<&GenericsArgs> {
330         match &self.mtype {
331             MatchType::Struct(gen_arg) | MatchType::Enum(gen_arg) => Some(gen_arg.as_ref()),
332             MatchType::Method(gen_arg) => gen_arg.as_ref().map(AsRef::as_ref),
333             _ => None,
334         }
335     }
into_generics(self) -> Option<GenericsArgs>336     pub(crate) fn into_generics(self) -> Option<GenericsArgs> {
337         match self.mtype {
338             MatchType::Struct(gen_arg) | MatchType::Enum(gen_arg) => Some(*gen_arg),
339             MatchType::Method(gen_arg) => gen_arg.map(|x| *x),
340             _ => None,
341         }
342     }
generics(&self) -> impl Iterator<Item = &TypeParameter>343     pub(crate) fn generics(&self) -> impl Iterator<Item = &TypeParameter> {
344         let opt = match self.mtype {
345             MatchType::Struct(ref gen_arg) | MatchType::Enum(ref gen_arg) => Some(gen_arg),
346             MatchType::Method(ref gen_arg) => gen_arg.as_ref(),
347             _ => None,
348         };
349         opt.into_iter().flat_map(|gen_arg| gen_arg.args())
350     }
resolved_generics(&self) -> impl Iterator<Item = &Ty>351     pub(crate) fn resolved_generics(&self) -> impl Iterator<Item = &Ty> {
352         let opt = match self.mtype {
353             MatchType::Struct(ref gen_arg) | MatchType::Enum(ref gen_arg) => Some(gen_arg),
354             MatchType::Method(ref gen_arg) => gen_arg.as_ref(),
355             _ => None,
356         };
357         opt.into_iter()
358             .flat_map(|gen_arg| gen_arg.args())
359             .filter_map(|ty_param| ty_param.resolved.as_ref())
360     }
resolve_generics(&mut self, types: &[Ty])361     pub(crate) fn resolve_generics(&mut self, types: &[Ty]) {
362         match self.mtype {
363             MatchType::Struct(ref mut gen_arg) | MatchType::Enum(ref mut gen_arg) => {
364                 gen_arg.apply_types(types);
365             }
366             _ => {}
367         };
368     }
369     // currently we can't resolve method's type parameter
generics_mut(&mut self) -> impl Iterator<Item = &mut TypeParameter>370     pub(crate) fn generics_mut(&mut self) -> impl Iterator<Item = &mut TypeParameter> {
371         let opt = match &mut self.mtype {
372             MatchType::Struct(gen_arg) | MatchType::Enum(gen_arg) => Some(&mut **gen_arg),
373             _ => None,
374         };
375         opt.into_iter().flat_map(|gen_arg| gen_arg.args_mut())
376     }
377 }
378 
379 /// The cursor position used by public search methods
380 #[derive(Debug, Clone, Copy)]
381 pub enum Location {
382     /// A byte offset in the file
383     Point(BytePos),
384     /// 1-based line and column indices.
385     Coords(Coordinate),
386 }
387 
388 impl From<BytePos> for Location {
from(val: BytePos) -> Location389     fn from(val: BytePos) -> Location {
390         Location::Point(val)
391     }
392 }
393 
394 impl From<usize> for Location {
from(val: usize) -> Location395     fn from(val: usize) -> Location {
396         Location::Point(BytePos(val))
397     }
398 }
399 
400 impl From<Coordinate> for Location {
from(val: Coordinate) -> Location401     fn from(val: Coordinate) -> Location {
402         Location::Coords(val)
403     }
404 }
405 
406 /// Internal cursor methods
407 pub trait LocationExt {
to_point(&self, src: &RawSource) -> Option<BytePos>408     fn to_point(&self, src: &RawSource) -> Option<BytePos>;
to_coords(&self, src: &RawSource) -> Option<Coordinate>409     fn to_coords(&self, src: &RawSource) -> Option<Coordinate>;
410 }
411 
412 impl LocationExt for Location {
to_point(&self, src: &RawSource) -> Option<BytePos>413     fn to_point(&self, src: &RawSource) -> Option<BytePos> {
414         match *self {
415             Location::Point(val) => Some(val),
416             Location::Coords(ref coords) => src.coords_to_point(coords),
417         }
418     }
419 
to_coords(&self, src: &RawSource) -> Option<Coordinate>420     fn to_coords(&self, src: &RawSource) -> Option<Coordinate> {
421         match *self {
422             Location::Coords(val) => Some(val),
423             Location::Point(point) => src.point_to_coords(point),
424         }
425     }
426 }
427 
428 impl fmt::Debug for Match {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result429     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
430         write!(
431             f,
432             "Match [{:?}, {:?}, {:?}, {:?}, {:?}, |{}|]",
433             self.matchstr,
434             self.filepath.display(),
435             self.point,
436             self.local,
437             self.mtype,
438             self.contextstr
439         )
440     }
441 }
442 
443 #[derive(Clone, PartialEq)]
444 pub struct Scope {
445     pub filepath: path::PathBuf,
446     pub point: BytePos,
447 }
448 
449 impl Scope {
new(path: path::PathBuf, pos: BytePos) -> Self450     pub fn new(path: path::PathBuf, pos: BytePos) -> Self {
451         Scope {
452             filepath: path,
453             point: pos,
454         }
455     }
456 
from_match(m: &Match) -> Scope457     pub fn from_match(m: &Match) -> Scope {
458         Scope {
459             filepath: m.filepath.clone(),
460             point: m.point,
461         }
462     }
463 }
464 
465 impl fmt::Debug for Scope {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result466     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
467         write!(f, "Scope [{:?}, {:?}]", self.filepath.display(), self.point)
468     }
469 }
470 
471 #[derive(Clone, Debug)]
472 pub struct RawSource {
473     pub code: String,
474     pub lines: RefCell<Vec<ByteRange>>,
475 }
476 
477 impl RawSource {
new(s: String) -> Self478     pub fn new(s: String) -> Self {
479         RawSource {
480             code: s,
481             lines: Default::default(),
482         }
483     }
484 
cache_lineoffsets(&self)485     fn cache_lineoffsets(&self) {
486         if self.lines.borrow().len() != 0 {
487             return;
488         }
489         let mut before = 0;
490         *self.lines.borrow_mut() = self
491             .code
492             .split('\n')
493             .map(|line| {
494                 let len = line.len() + 1;
495                 let res = ByteRange::new(before, before + len);
496                 before += len;
497                 res
498             })
499             .collect();
500     }
501 
coords_to_point(&self, coords: &Coordinate) -> Option<BytePos>502     pub fn coords_to_point(&self, coords: &Coordinate) -> Option<BytePos> {
503         self.cache_lineoffsets();
504         self.lines
505             .borrow()
506             .get(coords.row.zero_indexed().0 as usize)
507             .and_then(|&range| {
508                 let col = coords.col.0 as usize;
509                 if col < range.len() {
510                     Some(range.start + col.into())
511                 } else {
512                     None
513                 }
514             })
515     }
516 
point_to_coords(&self, point: BytePos) -> Option<Coordinate>517     pub fn point_to_coords(&self, point: BytePos) -> Option<Coordinate> {
518         self.cache_lineoffsets();
519         let lines = self.lines.borrow();
520         lines
521             .binary_search_by(|range| range.partial_cmp(&point).unwrap())
522             .ok()
523             .map(|idx| Coordinate::new(idx as u32 + 1, (point - lines[idx].start).0 as u32))
524     }
525 }
526 
527 #[derive(Clone, Debug)]
528 pub struct MaskedSource {
529     pub code: String,
530 }
531 
532 #[derive(Clone, Copy, Debug)]
533 pub struct Src<'c> {
534     pub src: &'c MaskedSource,
535     pub range: ByteRange,
536 }
537 
538 impl MaskedSource {
new(src: &str) -> MaskedSource539     pub fn new(src: &str) -> MaskedSource {
540         let idx: Vec<_> = codecleaner::code_chunks(&src).collect();
541         let code = scopes::mask_comments(src, &idx);
542         MaskedSource { code }
543     }
544 
as_src(&self) -> Src<'_>545     pub fn as_src(&self) -> Src<'_> {
546         self.get_src_from_start(BytePos::ZERO)
547     }
548 
get_src_from_start(&self, new_start: BytePos) -> Src<'_>549     pub fn get_src_from_start(&self, new_start: BytePos) -> Src<'_> {
550         Src {
551             src: self,
552             range: ByteRange::new(new_start, self.len().into()),
553         }
554     }
555 }
556 
557 pub struct MatchIter<'c> {
558     session: &'c Session<'c>,
559     matches: vec::IntoIter<Match>,
560 }
561 
562 impl<'c> Iterator for MatchIter<'c> {
563     type Item = Match;
564 
next(&mut self) -> Option<Match>565     fn next(&mut self) -> Option<Match> {
566         self.matches.next().map(|mut m| {
567             if m.coords.is_none() {
568                 let point = m.point;
569                 let src = self.session.load_raw_file(m.filepath.as_path());
570                 m.coords = src.point_to_coords(point);
571             }
572             m
573         })
574     }
575 }
576 
577 #[test]
coords_to_point_works()578 fn coords_to_point_works() {
579     let src = "
580 fn myfn() {
581     let a = 3;
582     print(a);
583 }";
584     let src = RawSource::new(src.into());
585     assert_eq!(
586         src.coords_to_point(&Coordinate::new(3, 5)),
587         Some(BytePos(18))
588     );
589 }
590 
591 #[test]
coords_to_point_lf_newline()592 fn coords_to_point_lf_newline() {
593     let src = "\n\
594                fn myfn() {\n\
595                let a = 3;\n\
596                print(a);\n\
597                }\n";
598     let src = RawSource::new(src.into());
599     assert_eq!(
600         src.coords_to_point(&Coordinate::new(3, 5)),
601         Some(BytePos(18))
602     );
603 }
604 
605 #[test]
coords_to_point_crlf_newline()606 fn coords_to_point_crlf_newline() {
607     let src = "\r\n\
608                fn myfn() {\r\n\
609                let a = 3;\r\n\
610                print(a);\r\n\
611                }\r\n";
612     let src = RawSource::new(src.into());
613     assert_eq!(
614         src.coords_to_point(&Coordinate::new(3, 5)),
615         Some(BytePos(20))
616     );
617 }
618 
619 #[test]
test_point_to_coords()620 fn test_point_to_coords() {
621     let src = "
622 fn myfn(b:usize) {
623    let a = 3;
624    if b == 12 {
625        let a = 24;
626        do_something_with(a);
627    }
628    do_something_with(a);
629 }
630 ";
631     fn round_trip_point_and_coords(src: &str, lineno: usize, charno: usize) {
632         let raw_src = RawSource::new(src.to_owned());
633         let point = raw_src
634             .coords_to_point(&Coordinate::new(lineno as u32, charno as u32))
635             .unwrap();
636         let coords = raw_src.point_to_coords(point).unwrap();
637         assert_eq!(coords, Coordinate::new(lineno as u32, charno as u32));
638     }
639     round_trip_point_and_coords(src, 4, 5);
640 }
641 
642 impl<'c> Src<'c> {
iter_stmts(&self) -> Fuse<StmtIndicesIter<'_>>643     pub fn iter_stmts(&self) -> Fuse<StmtIndicesIter<'_>> {
644         StmtIndicesIter::from_parts(self)
645     }
646 
shift_start(&self, shift: BytePos) -> Src<'c>647     pub fn shift_start(&self, shift: BytePos) -> Src<'c> {
648         Src {
649             src: self.src,
650             range: ByteRange::new(self.range.start + shift, self.range.end),
651         }
652     }
653 
change_length(&self, new_length: BytePos) -> Src<'c>654     pub fn change_length(&self, new_length: BytePos) -> Src<'c> {
655         Src {
656             src: self.src,
657             range: ByteRange::new(self.range.start, self.range.start + new_length),
658         }
659     }
660 
shift_range(&self, new_range: ByteRange) -> Src<'c>661     pub fn shift_range(&self, new_range: ByteRange) -> Src<'c> {
662         Src {
663             src: self.src,
664             range: new_range.shift(self.range.start),
665         }
666     }
667 }
668 
669 pub struct RangedRawSrc {
670     inner: Rc<RawSource>,
671     range: ByteRange,
672 }
673 
674 impl Deref for RangedRawSrc {
675     type Target = str;
deref(&self) -> &str676     fn deref(&self) -> &str {
677         &self.inner.code[self.range.to_range()]
678     }
679 }
680 
681 impl Deref for RawSource {
682     type Target = str;
deref(&self) -> &str683     fn deref(&self) -> &str {
684         &self.code
685     }
686 }
687 
688 impl Deref for MaskedSource {
689     type Target = str;
deref(&self) -> &str690     fn deref(&self) -> &str {
691         &self.code
692     }
693 }
694 
695 impl<'c> Deref for Src<'c> {
696     type Target = str;
deref(&self) -> &str697     fn deref(&self) -> &str {
698         &self.src.code[self.range.to_range()]
699     }
700 }
701 
702 /// Caches file contents for re-use between sessions.
703 ///
704 /// The file cache is an opaque blob outside of racer which contains maps of loaded and masked
705 /// files.
706 pub struct FileCache {
707     /// raw source for cached files
708     raw_map: RefCell<HashMap<path::PathBuf, Rc<RawSource>>>,
709 
710     /// masked source for cached files
711     ///
712     /// a version with comments and strings replaced by spaces, so that they
713     /// aren't found when scanning the source for signatures.
714     masked_map: RefCell<HashMap<path::PathBuf, Rc<MaskedSource>>>,
715 
716     /// The file loader
717     pub(crate) loader: Box<dyn FileLoader>,
718 }
719 
720 /// Used by the FileCache for loading files
721 ///
722 /// Implement one of these and pass it to `FileCache::new()` to override Racer's
723 /// file loading behavior.
724 pub trait FileLoader {
725     /// Load a single file
load_file(&self, path: &path::Path) -> io::Result<String>726     fn load_file(&self, path: &path::Path) -> io::Result<String>;
727 }
728 
729 /// Provide a blanket impl for Arc<T> since Rls uses that
730 impl<T: FileLoader> FileLoader for ::std::sync::Arc<T> {
load_file(&self, path: &path::Path) -> io::Result<String>731     fn load_file(&self, path: &path::Path) -> io::Result<String> {
732         (&self as &T).load_file(path)
733     }
734 }
735 
736 /// The default file loader
737 ///
738 /// Private since this shouldn't be needed outside of racer
739 struct DefaultFileLoader;
740 
741 impl FileLoader for DefaultFileLoader {
load_file(&self, path: &path::Path) -> io::Result<String>742     fn load_file(&self, path: &path::Path) -> io::Result<String> {
743         let mut rawbytes = Vec::new();
744         let mut f = File::open(path)?;
745         f.read_to_end(&mut rawbytes)?;
746 
747         // skip BOM bytes, if present
748         if rawbytes.len() > 2 && rawbytes[0..3] == [0xEF, 0xBB, 0xBF] {
749             str::from_utf8(&rawbytes[3..])
750                 .map(|s| s.to_owned())
751                 .map_err(|err| io::Error::new(io::ErrorKind::Other, err))
752         } else {
753             String::from_utf8(rawbytes).map_err(|err| io::Error::new(io::ErrorKind::Other, err))
754         }
755     }
756 }
757 
758 impl Default for FileCache {
default() -> FileCache759     fn default() -> FileCache {
760         FileCache::new(DefaultFileLoader)
761     }
762 }
763 
764 impl FileCache {
765     /// Create a new file cache
766     ///
767     /// In order to load files into the cache, please see
768     /// [`Session::cache_file_contents()`]
769     ///
770     /// [`Session::cache_file_contents()`]: struct.Session.html#method.cache_file_contents
new<L: FileLoader + 'static>(loader: L) -> FileCache771     pub fn new<L: FileLoader + 'static>(loader: L) -> FileCache {
772         FileCache {
773             raw_map: RefCell::new(HashMap::new()),
774             masked_map: RefCell::new(HashMap::new()),
775             loader: Box::new(loader),
776         }
777     }
778 
779     /// Remove specific files from the cache
780     ///
781     /// Returns true if a file was removed
remove_file<P: AsRef<path::Path>>(&self, path: &P) -> bool782     pub fn remove_file<P: AsRef<path::Path>>(&self, path: &P) -> bool {
783         let path = path.as_ref();
784         let mut raw = self.raw_map.borrow_mut();
785         let mut masked = self.masked_map.borrow_mut();
786         raw.remove(path).is_some() || masked.remove(path).is_some()
787     }
788 
789     /// Add/Replace a file in both versions.
cache_file_contents<P, T>(&self, filepath: P, buf: T) where T: Into<String>, P: Into<path::PathBuf>,790     fn cache_file_contents<P, T>(&self, filepath: P, buf: T)
791     where
792         T: Into<String>,
793         P: Into<path::PathBuf>,
794     {
795         let pathbuf = filepath.into();
796         let src = buf.into();
797         let masked_src = MaskedSource::new(&src);
798         self.raw_map
799             .borrow_mut()
800             .insert(pathbuf.clone(), Rc::new(RawSource::new(src)));
801         self.masked_map
802             .borrow_mut()
803             .insert(pathbuf, Rc::new(masked_src));
804     }
805 
load_file(&self, filepath: &path::Path) -> Rc<RawSource>806     fn load_file(&self, filepath: &path::Path) -> Rc<RawSource> {
807         if let Some(src) = self.raw_map.borrow().get(filepath) {
808             return src.clone();
809         }
810 
811         // nothing found, insert into cache
812         // Ugh, really need handle results on all these methods :(
813         let source = self
814             .loader
815             .load_file(filepath)
816             .expect(&format!("Failed load file {:?}", filepath));
817         let source = Rc::new(RawSource::new(source));
818         self.raw_map
819             .borrow_mut()
820             .insert(filepath.to_path_buf(), Rc::clone(&source));
821         source
822     }
823 
load_file_and_mask_comments(&self, filepath: &path::Path) -> Rc<MaskedSource>824     fn load_file_and_mask_comments(&self, filepath: &path::Path) -> Rc<MaskedSource> {
825         if let Some(src) = self.masked_map.borrow().get(filepath) {
826             return src.clone();
827         }
828         // nothing found, insert into cache
829         let src = self.load_file(filepath);
830         let msrc = Rc::new(MaskedSource::new(&src.code));
831         self.masked_map
832             .borrow_mut()
833             .insert(filepath.to_path_buf(), msrc.clone());
834         msrc
835     }
836 }
837 
838 /// Private methods for the Session type
839 pub trait SessionExt {
840     /// Request that a file is loaded into the cache
841     ///
842     /// This API is unstable and should not be used outside of Racer
load_raw_file(&self, _: &path::Path) -> Rc<RawSource>843     fn load_raw_file(&self, _: &path::Path) -> Rc<RawSource>;
844 
845     /// ranged version of load_raw_file
load_raw_src_ranged(&self, src: &Src<'_>, _: &path::Path) -> RangedRawSrc846     fn load_raw_src_ranged(&self, src: &Src<'_>, _: &path::Path) -> RangedRawSrc;
847 
848     /// Request that a file is loaded into the cache with comments masked
849     ///
850     /// This API is unstable and should not be used outside of Racer
load_source_file(&self, _: &path::Path) -> Rc<MaskedSource>851     fn load_source_file(&self, _: &path::Path) -> Rc<MaskedSource>;
852 }
853 
854 /// Context for a Racer operation
855 pub struct Session<'c> {
856     /// Cache for files
857     ///
858     /// The file cache is used within a session to prevent multiple reads. It is
859     /// borrowed here in order to support reuse across Racer operations.
860     cache: &'c FileCache,
861     /// Cache for generic impls
862     pub generic_impls: RefCell<HashMap<(path::PathBuf, BytePos), Vec<Rc<ImplHeader>>>>,
863     pub project_model: Box<dyn ProjectModelProvider + 'c>,
864 }
865 
866 impl<'c> fmt::Debug for Session<'c> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result867     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
868         write!(f, "Session {{ .. }}")
869     }
870 }
871 
872 impl<'c> Session<'c> {
873     /// Create a Session for use in Racer operations
874     ///
875     /// * `cache` is a reference to a `FileCache`. It's take by reference for
876     ///   use across racer operations.
877     ///
878     /// # Examples
879     ///
880     /// ```
881     /// extern crate racer;
882     ///
883     /// let cache = racer::FileCache::default();
884     /// let session = racer::Session::new(&cache, None);
885     /// ```
886     ///
887     /// [`FileCache`]: struct.FileCache.html
888     #[cfg(feature = "metadata")]
new(cache: &'c FileCache, project_path: Option<&path::Path>) -> Session<'c>889     pub fn new(cache: &'c FileCache, project_path: Option<&path::Path>) -> Session<'c> {
890         let project_model = crate::metadata::project_model(project_path);
891         Session::with_project_model(cache, project_model)
892     }
893 
with_project_model( cache: &'c FileCache, project_model: Box<dyn ProjectModelProvider + 'c>, ) -> Session<'c>894     pub fn with_project_model(
895         cache: &'c FileCache,
896         project_model: Box<dyn ProjectModelProvider + 'c>,
897     ) -> Session<'c> {
898         Session {
899             cache,
900             generic_impls: Default::default(),
901             project_model,
902         }
903     }
904     /// Specify the contents of a file to be used in completion operations
905     ///
906     /// The path to the file and the file's contents must both be specified.
907     ///
908     /// # Examples
909     ///
910     /// ```
911     /// extern crate racer;
912     ///
913     /// let cache = racer::FileCache::default();
914     /// let session = racer::Session::new(&cache, None);
915     ///
916     /// session.cache_file_contents("foo.rs", "pub struct Foo;\\n");
917     /// ```
cache_file_contents<T, P>(&self, filepath: P, buf: T) where T: Into<String>, P: Into<path::PathBuf>,918     pub fn cache_file_contents<T, P>(&self, filepath: P, buf: T)
919     where
920         T: Into<String>,
921         P: Into<path::PathBuf>,
922     {
923         self.cache.cache_file_contents(filepath, buf);
924     }
925 
contains_file<P: AsRef<path::Path>>(&self, path: P) -> bool926     pub fn contains_file<P: AsRef<path::Path>>(&self, path: P) -> bool {
927         let path = path.as_ref();
928         let raw = self.cache.raw_map.borrow();
929         let masked = self.cache.masked_map.borrow();
930         raw.contains_key(path) && masked.contains_key(path)
931     }
932 }
933 
934 impl<'c> SessionExt for Session<'c> {
load_raw_file(&self, filepath: &path::Path) -> Rc<RawSource>935     fn load_raw_file(&self, filepath: &path::Path) -> Rc<RawSource> {
936         self.cache.load_file(filepath)
937     }
938 
load_raw_src_ranged(&self, src: &Src<'_>, filepath: &path::Path) -> RangedRawSrc939     fn load_raw_src_ranged(&self, src: &Src<'_>, filepath: &path::Path) -> RangedRawSrc {
940         let inner = self.cache.load_file(filepath);
941         RangedRawSrc {
942             inner,
943             range: src.range,
944         }
945     }
946 
load_source_file(&self, filepath: &path::Path) -> Rc<MaskedSource>947     fn load_source_file(&self, filepath: &path::Path) -> Rc<MaskedSource> {
948         self.cache.load_file_and_mask_comments(filepath)
949     }
950 }
951 
952 /// Get the racer point of a line/character number pair for a file.
to_point<P>(coords: Coordinate, path: P, session: &Session<'_>) -> Option<BytePos> where P: AsRef<path::Path>,953 pub fn to_point<P>(coords: Coordinate, path: P, session: &Session<'_>) -> Option<BytePos>
954 where
955     P: AsRef<path::Path>,
956 {
957     Location::from(coords).to_point(&session.load_raw_file(path.as_ref()))
958 }
959 
960 /// Get the racer point of a line/character number pair for a file.
to_coords<P>(point: BytePos, path: P, session: &Session<'_>) -> Option<Coordinate> where P: AsRef<path::Path>,961 pub fn to_coords<P>(point: BytePos, path: P, session: &Session<'_>) -> Option<Coordinate>
962 where
963     P: AsRef<path::Path>,
964 {
965     Location::from(point).to_coords(&session.load_raw_file(path.as_ref()))
966 }
967 
968 /// Find completions for a fully qualified name like `std::io::`
969 ///
970 /// Searchs are started relative to `path`.
971 ///
972 /// * `query` - is the fqn to search for
973 /// * `path` - the directory to start searching in
974 /// * `session` - reference to a racer::Session
975 ///
976 /// ```no_run
977 /// extern crate racer;
978 ///
979 /// let path = std::path::Path::new(".");
980 /// let cache = racer::FileCache::default();
981 /// let session = racer::Session::new(&cache, Some(path));
982 ///
983 /// let m = racer::complete_fully_qualified_name(
984 ///     "std::fs::canon",
985 ///     &path,
986 ///     &session
987 /// ).next().unwrap();
988 ///
989 /// assert_eq!(&m.matchstr[..], "canonicalize");
990 /// assert_eq!(m.mtype, racer::MatchType::Function);
991 /// ```
992 #[inline]
complete_fully_qualified_name<'c, S, P>( query: S, path: P, session: &'c Session<'_>, ) -> MatchIter<'c> where S: AsRef<str>, P: AsRef<path::Path>,993 pub fn complete_fully_qualified_name<'c, S, P>(
994     query: S,
995     path: P,
996     session: &'c Session<'_>,
997 ) -> MatchIter<'c>
998 where
999     S: AsRef<str>,
1000     P: AsRef<path::Path>,
1001 {
1002     let mut matches = complete_fully_qualified_name_(query.as_ref(), path.as_ref(), session);
1003     matches.dedup_by(|a, b| a.is_same_as(b));
1004 
1005     MatchIter {
1006         matches: matches.into_iter(),
1007         session,
1008     }
1009 }
1010 
1011 /// Actual implementation without generic bounds
complete_fully_qualified_name_( query: &str, path: &path::Path, session: &Session<'_>, ) -> Vec<Match>1012 fn complete_fully_qualified_name_(
1013     query: &str,
1014     path: &path::Path,
1015     session: &Session<'_>,
1016 ) -> Vec<Match> {
1017     let p: Vec<&str> = query.split("::").collect();
1018 
1019     let mut matches = Vec::new();
1020 
1021     for m in nameres::do_file_search(p[0], path, session) {
1022         if p.len() == 1 {
1023             matches.push(m);
1024         } else {
1025             let external_search_matches = nameres::do_external_search(
1026                 &p[1..],
1027                 &m.filepath,
1028                 m.point,
1029                 SearchType::StartsWith,
1030                 Namespace::Path,
1031                 &session,
1032             );
1033 
1034             for m in external_search_matches {
1035                 matches.push(m);
1036             }
1037         }
1038     }
1039 
1040     matches
1041 }
1042 
1043 /// Search for completion at position in a file
1044 ///
1045 /// * `src` - the file contents to search in
1046 /// * `filepath` - path to file containing `src`
1047 /// * `pos` - byte offset in file with path/expr to complete
1048 /// * `session` - a racer::Session
1049 ///
1050 /// # Examples
1051 ///
1052 /// ```
1053 /// extern crate racer;
1054 ///
1055 /// # fn main() {
1056 /// let src = "
1057 /// fn apple() {
1058 /// }
1059 ///
1060 /// fn main() {
1061 ///     let b = ap
1062 /// }";
1063 ///
1064 /// println!("{:?}", src);
1065 ///
1066 /// let cache = racer::FileCache::default();
1067 /// let session = racer::Session::new(&cache, None);
1068 ///
1069 /// session.cache_file_contents("lib.rs", src);
1070 ///
1071 /// let got = racer::complete_from_file("lib.rs", racer::Location::from(43), &session)
1072 ///     .nth(0).unwrap();
1073 /// assert_eq!("apple", got.matchstr);
1074 /// assert_eq!(got.mtype, racer::MatchType::Function);
1075 ///
1076 /// # }
1077 /// ```
complete_from_file<'c, P, C>( filepath: P, cursor: C, session: &'c Session<'_>, ) -> MatchIter<'c> where P: AsRef<path::Path>, C: Into<Location>,1078 pub fn complete_from_file<'c, P, C>(
1079     filepath: P,
1080     cursor: C,
1081     session: &'c Session<'_>,
1082 ) -> MatchIter<'c>
1083 where
1084     P: AsRef<path::Path>,
1085     C: Into<Location>,
1086 {
1087     let mut matches = complete_from_file_(filepath.as_ref(), cursor.into(), session);
1088     matches.sort_by(|a, b| a.matchstr.cmp(&b.matchstr).then(a.point.cmp(&b.point)));
1089     matches.dedup_by(|a, b| a.is_same_as(b));
1090 
1091     MatchIter {
1092         matches: matches.into_iter(),
1093         session,
1094     }
1095 }
1096 
complete_from_file_( filepath: &path::Path, cursor: Location, session: &Session<'_>, ) -> Vec<Match>1097 fn complete_from_file_(
1098     filepath: &path::Path,
1099     cursor: Location,
1100     session: &Session<'_>,
1101 ) -> Vec<Match> {
1102     let src = session.load_source_file(filepath);
1103     let raw_src = session.load_raw_file(filepath);
1104     let src_text = &src.as_src()[..];
1105     // TODO return result
1106     let pos = match cursor.to_point(&raw_src) {
1107         Some(pos) => pos,
1108         None => {
1109             debug!("Failed to convert cursor to point");
1110             return Vec::new();
1111         }
1112     };
1113     let start = scopes::get_start_of_search_expr(src_text, pos);
1114     let expr = &src_text[start.0..pos.0];
1115     let (contextstr, searchstr, completetype) = scopes::split_into_context_and_completion(expr);
1116 
1117     debug!(
1118         "{:?}: contextstr is |{}|, searchstr is |{}|",
1119         completetype, contextstr, searchstr
1120     );
1121 
1122     let mut out = Vec::new();
1123 
1124     match completetype {
1125         CompletionType::Path => {
1126             let (stmtstart, stmt) = &scopes::get_current_stmt(src.as_src(), pos);
1127             debug!("Complete path with stmt: {:?}", stmt);
1128             // when in the function ident position, only look for methods
1129             // from a trait to complete.
1130             if util::in_fn_name(stmt) {
1131                 trace!("Path is in fn declaration: `{}`", expr);
1132                 return nameres::resolve_method(
1133                     pos,
1134                     src.as_src(),
1135                     expr,
1136                     filepath,
1137                     SearchType::StartsWith,
1138                     session,
1139                     &ImportInfo::default(),
1140                 );
1141             }
1142             let (path, namespace) = if let Some(use_start) = scopes::use_stmt_start(stmt) {
1143                 let path = scopes::construct_path_from_use_tree(&stmt[use_start.0..]);
1144                 (path, Namespace::Path)
1145             } else if scopes::is_extern_crate(stmt) {
1146                 return fileres::search_crate_names(
1147                     searchstr,
1148                     SearchType::StartsWith,
1149                     filepath,
1150                     false,
1151                     session,
1152                 );
1153             } else if let Some(str_path) = scopes::is_in_struct_ctor(src.as_src(), *stmtstart, pos)
1154             {
1155                 let path = scopes::expr_to_path(&src[str_path.to_range()]).0;
1156                 return nameres::get_struct_fields(
1157                     &path,
1158                     searchstr,
1159                     filepath,
1160                     pos,
1161                     SearchType::StartsWith,
1162                     session,
1163                 );
1164             } else {
1165                 scopes::expr_to_path(expr)
1166             };
1167             debug!("path: {:?}, prefix: {:?}", path, path.prefix);
1168             out.extend(nameres::resolve_path(
1169                 &path,
1170                 filepath,
1171                 pos,
1172                 SearchType::StartsWith,
1173                 namespace,
1174                 session,
1175                 &ImportInfo::default(),
1176             ));
1177         }
1178         CompletionType::Field => {
1179             let context = ast::get_type_of(contextstr.to_owned(), filepath, pos, session);
1180             debug!("complete_from_file context is {:?}", context);
1181             if let Some(ty) = context {
1182                 out.extend(nameres::get_field_matches_from_ty(
1183                     ty,
1184                     searchstr,
1185                     SearchType::StartsWith,
1186                     session,
1187                 ));
1188             }
1189         }
1190     }
1191 
1192     out
1193 }
1194 
1195 /// Finds if the statement where cursor lies is a `use` statement.
1196 ///
1197 /// # Examples
1198 ///
1199 /// ```
1200 /// extern crate racer;
1201 /// extern crate env_logger;
1202 ///
1203 ///
1204 /// # fn main() {
1205 /// let _ = env_logger::init();
1206 /// let cache = racer::FileCache::default();
1207 /// let session = racer::Session::new(&cache, None);
1208 ///
1209 /// // This is the file where we request completion from
1210 /// let src = stringify! {
1211 ///    use sub::foo;
1212 ///    use sub::{
1213 ///         bar
1214 ///    };
1215 ///    pub(crate) use sub::baz;
1216 /// };
1217 ///
1218 /// // Load files into cache to prevent trying to read from disk
1219 /// session.cache_file_contents("lib.rs", src);
1220 ///
1221 /// assert_eq!(racer::is_use_stmt("lib.rs", racer::Location::from(9), &session), true);
1222 /// assert_eq!(racer::is_use_stmt("lib.rs", racer::Location::from(28), &session), true);
1223 /// assert_eq!(racer::is_use_stmt("lib.rs", racer::Location::from(5000), &session), false);
1224 /// # }
1225 /// ```
is_use_stmt<P, C>(file_path: P, cursor: C, session: &Session<'_>) -> bool where P: AsRef<path::Path>, C: Into<Location>,1226 pub fn is_use_stmt<P, C>(file_path: P, cursor: C, session: &Session<'_>) -> bool
1227 where
1228     P: AsRef<path::Path>,
1229     C: Into<Location>,
1230 {
1231     let file_path = file_path.as_ref();
1232     let src = session.load_source_file(file_path);
1233     let raw_src = session.load_raw_file(file_path);
1234     let pos = match cursor.into().to_point(&raw_src) {
1235         Some(pos) => pos,
1236         None => return false,
1237     };
1238 
1239     if src.bytes().len() <= pos.0 {
1240         return false;
1241     }
1242 
1243     let line = &scopes::get_current_stmt(src.as_src(), pos).1;
1244     scopes::use_stmt_start(line).is_some()
1245 }
1246 
1247 /// Find the definition for item at given a file, source, and cursor index
1248 ///
1249 /// # Examples
1250 ///
1251 /// ```
1252 /// extern crate racer;
1253 /// extern crate env_logger;
1254 ///
1255 /// use std::path::Path;
1256 ///
1257 /// # fn main() {
1258 /// let _ = env_logger::init();
1259 /// let cache = racer::FileCache::default();
1260 /// let session = racer::Session::new(&cache, None);
1261 ///
1262 /// // This is the file where we request completion from
1263 /// let src = r"
1264 ///    mod sub;
1265 ///    use sub::foo;
1266 ///    fn main() {
1267 ///        foo();
1268 ///    };
1269 /// ";
1270 ///
1271 /// // This is the submodule where the definition is found
1272 /// let sub = r"pub fn foo() {}";
1273 ///
1274 /// // Load files into cache to prevent trying to read from disk
1275 /// session.cache_file_contents("sub.rs", sub);
1276 /// session.cache_file_contents("lib.rs", src);
1277 ///
1278 /// // Search for the definition. 52 is the byte offset in `src`.
1279 /// // Specifically, this asks for the definition of `foo()`.
1280 /// let m = racer::find_definition("lib.rs", racer::Location::from(52), &session)
1281 ///               .expect("find definition returns a match");
1282 ///
1283 /// // Should have found definition in the "sub.rs" file
1284 /// assert_eq!(m.filepath, Path::new("sub.rs"));
1285 /// // The definition should be for foo
1286 /// assert_eq!(&m.matchstr[..], "foo");
1287 /// // The definition should be a function
1288 /// assert_eq!(m.mtype, racer::MatchType::Function);
1289 /// # }
1290 /// ```
find_definition<P, C>(filepath: P, cursor: C, session: &Session<'_>) -> Option<Match> where P: AsRef<path::Path>, C: Into<Location>,1291 pub fn find_definition<P, C>(filepath: P, cursor: C, session: &Session<'_>) -> Option<Match>
1292 where
1293     P: AsRef<path::Path>,
1294     C: Into<Location>,
1295 {
1296     find_definition_(filepath.as_ref(), cursor.into(), session).map(|mut m| {
1297         if m.coords.is_none() {
1298             let point = m.point;
1299             let src = session.load_raw_file(m.filepath.as_path());
1300             m.coords = src.point_to_coords(point);
1301         }
1302         m
1303     })
1304 }
1305 
find_definition_( filepath: &path::Path, cursor: Location, session: &Session<'_>, ) -> Option<Match>1306 pub fn find_definition_(
1307     filepath: &path::Path,
1308     cursor: Location,
1309     session: &Session<'_>,
1310 ) -> Option<Match> {
1311     let src = session.load_source_file(filepath);
1312     let src_txt = &src[..];
1313     // TODO return result
1314     let pos = match cursor.to_point(&session.load_raw_file(filepath)) {
1315         Some(pos) => pos,
1316         None => {
1317             debug!("Failed to convert cursor to point");
1318             return None;
1319         }
1320     };
1321 
1322     // Make sure `src` is in the cache
1323     let range = scopes::expand_search_expr(src_txt, pos);
1324     let expr = &src[range.to_range()];
1325     let (contextstr, searchstr, completetype) = scopes::split_into_context_and_completion(expr);
1326     debug!(
1327         "find_definition_ for |{:?}| |{:?}| {:?}",
1328         contextstr, searchstr, completetype
1329     );
1330 
1331     match completetype {
1332         CompletionType::Path => {
1333             let (stmtstart, stmt) = &scopes::get_current_stmt(src.as_src(), range.end);
1334             let (path, namespace) = if let Some(use_start) = scopes::use_stmt_start(stmt) {
1335                 let path = scopes::construct_path_from_use_tree(&stmt[use_start.0..]);
1336                 (path, Namespace::Path)
1337             } else if let Some(str_path) = scopes::is_in_struct_ctor(src.as_src(), *stmtstart, pos)
1338             {
1339                 let path = scopes::expr_to_path(&src[str_path.to_range()]).0;
1340                 return nameres::get_struct_fields(
1341                     &path,
1342                     searchstr,
1343                     filepath,
1344                     pos,
1345                     SearchType::StartsWith,
1346                     session,
1347                 )
1348                 .into_iter()
1349                 .next();
1350             } else {
1351                 scopes::expr_to_path(expr)
1352             };
1353             debug!("[find_definition_] Path: {:?}", path);
1354             nameres::resolve_path(
1355                 &path,
1356                 filepath,
1357                 pos,
1358                 SearchType::ExactMatch,
1359                 namespace,
1360                 session,
1361                 &ImportInfo::default(),
1362             )
1363             .into_iter()
1364             .nth(0)
1365         }
1366         CompletionType::Field => {
1367             let context = ast::get_type_of(contextstr.to_owned(), filepath, pos, session);
1368             debug!("context is {:?}", context);
1369             let only_method = src[range.end.0..].starts_with('(');
1370             context.and_then(|ty| {
1371                 nameres::get_field_matches_from_ty(ty, searchstr, SearchType::ExactMatch, session)
1372                     .into_iter()
1373                     .filter(|m| !only_method || m.mtype.is_function())
1374                     .next()
1375             })
1376         }
1377     }
1378 }
1379 
1380 #[cfg(test)]
1381 mod tests {
1382     use super::FileCache;
1383     use super::{Session, SessionExt};
1384     use std::path::Path;
1385 
1386     #[test]
overwriting_cached_files()1387     fn overwriting_cached_files() {
1388         let src1 = "src1";
1389         let src2 = "src2";
1390         let src3 = "src3";
1391         let src4 = "src4";
1392 
1393         // Need session and path to cache files
1394         let path = Path::new("not_on_disk");
1395         let cache = FileCache::default();
1396 
1397         // Cache contents for a file and assert that load_file and load_file_and_mask_comments return
1398         // the newly cached contents.
1399         macro_rules! cache_and_assert {
1400             ($src: ident) => {{
1401                 let session = Session::new(&cache, Some(path));
1402                 session.cache_file_contents(path, $src);
1403                 assert_eq!($src, &session.load_raw_file(path)[..]);
1404                 assert_eq!($src, &session.load_source_file(path).code[..]);
1405             }};
1406         }
1407 
1408         // Check for all srcN
1409         cache_and_assert!(src1);
1410         cache_and_assert!(src2);
1411         cache_and_assert!(src3);
1412         cache_and_assert!(src4);
1413     }
1414 }
1415