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