1 //! Type inference
2 //! THIS MODULE IS ENTIRELY TOO UGLY SO REALLY NEADS REFACTORING(kngwyu)
3 use crate::ast;
4 use crate::ast_types::{Pat, Ty};
5 use crate::core;
6 use crate::core::{
7     BytePos, ByteRange, Match, MatchType, Namespace, Scope, SearchType, Session, SessionExt, Src,
8 };
9 use crate::matchers;
10 use crate::nameres;
11 use crate::primitive::PrimKind;
12 use crate::scopes;
13 use crate::util::{self, txt_matches};
14 use rustc_ast::ast::BinOpKind;
15 use std::path::Path;
16 
17 // Removes the body of the statement (anything in the braces {...}), leaving just
18 // the header
generate_skeleton_for_parsing(src: &str) -> Option<String>19 pub fn generate_skeleton_for_parsing(src: &str) -> Option<String> {
20     src.find('{').map(|n| src[..=n].to_owned() + "}")
21 }
22 
23 /// Get the trait name implementing which overrides the operator `op`
24 /// For comparison operators, it is `bool`
get_operator_trait(op: BinOpKind) -> &'static str25 pub(crate) fn get_operator_trait(op: BinOpKind) -> &'static str {
26     match op {
27         BinOpKind::Add => "Add",
28         BinOpKind::Sub => "Sub",
29         BinOpKind::Mul => "Mul",
30         BinOpKind::Div => "Div",
31         BinOpKind::Rem => "Rem",
32         BinOpKind::And => "And",
33         BinOpKind::Or => "Or",
34         BinOpKind::BitXor => "BitXor",
35         BinOpKind::BitAnd => "BitAnd",
36         BinOpKind::BitOr => "BitOr",
37         BinOpKind::Shl => "Shl",
38         BinOpKind::Shr => "Shr",
39         _ => "bool",
40     }
41 }
42 
43 // TODO(kngwyu): use libsyntax parser
first_param_is_self(blob: &str) -> bool44 pub fn first_param_is_self(blob: &str) -> bool {
45     // Restricted visibility introduces the possibility of `pub(in ...)` at the start
46     // of a method declaration. To counteract this, we restrict the search to only
47     // look at text _after_ the visibility declaration.
48     //
49     // Having found the end of the visibility declaration, we now start the search
50     // for method parameters.
51     let blob = util::trim_visibility(blob);
52 
53     // skip generic arg
54     // consider 'pub fn map<U, F: FnOnce(T) -> U>(self, f: F)'
55     // we have to match the '>'
56     match blob.find('(') {
57         None => false,
58         Some(probable_param_start) => {
59             let skip_generic = match blob.find('<') {
60                 None => 0,
61                 Some(generic_start) if generic_start < probable_param_start => {
62                     let mut level = 0;
63                     let mut prev = ' ';
64                     let mut skip_generic = 0;
65                     for (i, c) in blob[generic_start..].char_indices() {
66                         match c {
67                             '<' => level += 1,
68                             '>' if prev == '-' => (),
69                             '>' => level -= 1,
70                             _ => (),
71                         }
72                         prev = c;
73                         if level == 0 {
74                             skip_generic = i;
75                             break;
76                         }
77                     }
78                     skip_generic
79                 }
80                 Some(..) => 0,
81             };
82             if let Some(start) = blob[skip_generic..].find('(') {
83                 let start = BytePos::from(skip_generic + start).increment();
84                 let end = scopes::find_closing_paren(blob, start);
85                 let is_self = txt_matches(SearchType::ExactMatch, "self", &blob[start.0..end.0]);
86                 trace!(
87                     "searching fn args for self: |{}| {}",
88                     &blob[start.0..end.0],
89                     is_self
90                 );
91                 return is_self;
92             }
93             false
94         }
95     }
96 }
97 
98 #[test]
generates_skeleton_for_mod()99 fn generates_skeleton_for_mod() {
100     let src = "mod foo { blah }";
101     let out = generate_skeleton_for_parsing(src).unwrap();
102     assert_eq!("mod foo {}", out);
103 }
104 
get_type_of_self_arg(m: &Match, msrc: Src<'_>, session: &Session<'_>) -> Option<Ty>105 fn get_type_of_self_arg(m: &Match, msrc: Src<'_>, session: &Session<'_>) -> Option<Ty> {
106     debug!("get_type_of_self_arg {:?}", m);
107     get_type_of_self(m.point, &m.filepath, m.local, msrc, session)
108 }
109 
110 // TODO(kngwyu): parse correctly
get_type_of_self( point: BytePos, filepath: &Path, local: bool, msrc: Src<'_>, session: &Session<'_>, ) -> Option<Ty>111 pub fn get_type_of_self(
112     point: BytePos,
113     filepath: &Path,
114     local: bool,
115     msrc: Src<'_>,
116     session: &Session<'_>,
117 ) -> Option<Ty> {
118     let start = scopes::find_impl_start(msrc, point, BytePos::ZERO)?;
119     let decl = generate_skeleton_for_parsing(&msrc.shift_start(start))?;
120     debug!("get_type_of_self_arg impl skeleton |{}|", decl);
121     if decl.starts_with("impl") {
122         // we have to do 2 operations around generics here
123         // 1. Checks if self's type is T
124         // 2. Checks if self's type contains T
125         let scope_start = start + decl.len().into();
126         let implres = ast::parse_impl(decl, filepath, start, local, scope_start)?;
127         if let Some((_, param)) = implres.generics().search_param_by_path(implres.self_path()) {
128             if let Some(resolved) = param.resolved() {
129                 return Some(resolved.to_owned());
130             }
131             let mut m = param.to_owned().into_match();
132             m.local = local;
133             return Some(Ty::Match(m));
134         }
135         debug!("get_type_of_self_arg implres |{:?}|", implres);
136         nameres::resolve_path(
137             implres.self_path(),
138             filepath,
139             start,
140             SearchType::ExactMatch,
141             Namespace::Type,
142             session,
143             &matchers::ImportInfo::default(),
144         )
145         .into_iter()
146         .nth(0)
147         .map(|mut m| {
148             match &mut m.mtype {
149                 MatchType::Enum(gen) | MatchType::Struct(gen) => {
150                     for (i, param) in implres.generics.0.into_iter().enumerate() {
151                         gen.add_bound(i, param.bounds);
152                     }
153                 }
154                 _ => {}
155             }
156             Ty::Match(m)
157         })
158     } else {
159         // // must be a trait
160         ast::parse_trait(decl).name.and_then(|name| {
161             Some(Ty::Match(Match {
162                 matchstr: name,
163                 filepath: filepath.into(),
164                 point: start,
165                 coords: None,
166                 local: local,
167                 mtype: core::MatchType::Trait,
168                 contextstr: matchers::first_line(&msrc[start.0..]),
169                 docs: String::new(),
170             }))
171         })
172     }
173 }
174 
get_type_of_fnarg(m: Match, session: &Session<'_>) -> Option<Ty>175 fn get_type_of_fnarg(m: Match, session: &Session<'_>) -> Option<Ty> {
176     let Match {
177         matchstr,
178         filepath,
179         point,
180         mtype,
181         ..
182     } = m;
183     let (pat, ty) = *match mtype {
184         MatchType::FnArg(a) => a,
185         _ => return None,
186     };
187     resolve_lvalue_ty(pat, ty, &matchstr, &filepath, point, session)
188 }
189 
get_type_of_let_expr(m: Match, session: &Session<'_>) -> Option<Ty>190 fn get_type_of_let_expr(m: Match, session: &Session<'_>) -> Option<Ty> {
191     let Match {
192         mtype,
193         contextstr,
194         filepath,
195         point,
196         ..
197     } = m;
198     let let_start = match mtype {
199         MatchType::Let(s) => s,
200         _ => return None,
201     };
202     debug!("get_type_of_let_expr calling parse_let |{}|", contextstr);
203     let pos = point - let_start;
204     let scope = Scope {
205         filepath,
206         point: let_start,
207     };
208     ast::get_let_type(contextstr, pos, scope, session)
209 }
210 
211 /// Decide l_value's type given r_value and ident query
resolve_lvalue_ty<'a>( l_value: Pat, r_value: Option<Ty>, query: &str, fpath: &Path, pos: BytePos, session: &Session<'_>, ) -> Option<Ty>212 pub(crate) fn resolve_lvalue_ty<'a>(
213     l_value: Pat,
214     r_value: Option<Ty>,
215     query: &str,
216     fpath: &Path,
217     pos: BytePos,
218     session: &Session<'_>,
219 ) -> Option<Ty> {
220     match l_value {
221         Pat::Ident(_bi, name) => {
222             if name != query {
223                 return None;
224             }
225             r_value
226         }
227         Pat::Tuple(pats) => {
228             if let Ty::Tuple(ty) = r_value? {
229                 for (p, t) in pats.into_iter().zip(ty) {
230                     let ret = try_continue!(resolve_lvalue_ty(p, t, query, fpath, pos, session,));
231                     return Some(ret);
232                 }
233             }
234             None
235         }
236         Pat::Ref(pat, _) => {
237             if let Some(ty) = r_value {
238                 if let Ty::RefPtr(ty, _) = ty {
239                     resolve_lvalue_ty(*pat, Some(*ty), query, fpath, pos, session)
240                 } else {
241                     resolve_lvalue_ty(*pat, Some(ty), query, fpath, pos, session)
242                 }
243             } else {
244                 resolve_lvalue_ty(*pat, None, query, fpath, pos, session)
245             }
246         }
247         Pat::TupleStruct(path, pats) => {
248             let ma = ast::find_type_match(&path, fpath, pos, session)?;
249             match &ma.mtype {
250                 MatchType::Struct(_generics) => {
251                     for (pat, (_, _, t)) in
252                         pats.into_iter().zip(get_tuplestruct_fields(&ma, session))
253                     {
254                         let ret =
255                             try_continue!(resolve_lvalue_ty(pat, t, query, fpath, pos, session));
256                         return Some(ret);
257                     }
258                     None
259                 }
260                 MatchType::EnumVariant(enum_) => {
261                     let generics = if let Some(Ty::Match(match_)) = r_value.map(Ty::dereference) {
262                         match_.into_generics()
263                     } else {
264                         enum_.to_owned().and_then(|ma| ma.into_generics())
265                     };
266                     for (pat, (_, _, mut t)) in
267                         pats.into_iter().zip(get_tuplestruct_fields(&ma, session))
268                     {
269                         debug!(
270                             "Hi! I'm in enum and l: {:?}\n  r: {:?}\n gen: {:?}",
271                             pat, t, generics
272                         );
273                         if let Some(ref gen) = generics {
274                             t = t.map(|ty| ty.replace_by_resolved_generics(&gen));
275                         }
276                         let ret =
277                             try_continue!(resolve_lvalue_ty(pat, t, query, fpath, pos, session));
278                         return Some(ret);
279                     }
280                     None
281                 }
282                 _ => None,
283             }
284         }
285         // Let's implement after #946 solved
286         Pat::Struct(path, _) => {
287             let item = ast::find_type_match(&path, fpath, pos, session)?;
288             if !item.mtype.is_struct() {
289                 return None;
290             }
291             None
292         }
293         _ => None,
294     }
295 }
296 
get_type_of_for_arg(m: &Match, session: &Session<'_>) -> Option<Ty>297 fn get_type_of_for_arg(m: &Match, session: &Session<'_>) -> Option<Ty> {
298     let for_start = match &m.mtype {
299         MatchType::For(pos) => *pos,
300         _ => {
301             warn!("[get_type_of_for_expr] invalid match type: {:?}", m.mtype);
302             return None;
303         }
304     };
305     // HACK: use outer scope when getting in ~ expr's type
306     let scope = Scope::new(m.filepath.clone(), for_start);
307     let ast::ForStmtVisitor {
308         for_pat, in_expr, ..
309     } = ast::parse_for_stmt(m.contextstr.clone(), scope, session);
310     debug!(
311         "[get_type_of_for_expr] match: {:?}, for: {:?}, in: {:?},",
312         m, for_pat, in_expr
313     );
314     fn get_item(ty: Ty, session: &Session<'_>) -> Option<Ty> {
315         match ty {
316             Ty::Match(ma) => nameres::get_iter_item(&ma, session),
317             Ty::PathSearch(paths) => {
318                 nameres::get_iter_item(&paths.resolve_as_match(session)?, session)
319             }
320             Ty::RefPtr(ty, _) => get_item(*ty, session),
321             _ => None,
322         }
323     }
324     resolve_lvalue_ty(
325         for_pat?,
326         in_expr.and_then(|ty| get_item(ty, session)),
327         &m.matchstr,
328         &m.filepath,
329         m.point,
330         session,
331     )
332 }
333 
get_type_of_if_let(m: &Match, session: &Session<'_>, start: BytePos) -> Option<Ty>334 fn get_type_of_if_let(m: &Match, session: &Session<'_>, start: BytePos) -> Option<Ty> {
335     // HACK: use outer scope when getting r-value's type
336     let scope = Scope::new(m.filepath.clone(), start);
337     let ast::IfLetVisitor {
338         let_pat, rh_expr, ..
339     } = ast::parse_if_let(m.contextstr.clone(), scope, session);
340     debug!(
341         "[get_type_of_if_let] match: {:?}\n  let: {:?}\n  rh: {:?},",
342         m, let_pat, rh_expr,
343     );
344     resolve_lvalue_ty(
345         let_pat?,
346         rh_expr,
347         &m.matchstr,
348         &m.filepath,
349         m.point,
350         session,
351     )
352 }
353 
get_struct_field_type( fieldname: &str, structmatch: &Match, session: &Session<'_>, ) -> Option<Ty>354 pub fn get_struct_field_type(
355     fieldname: &str,
356     structmatch: &Match,
357     session: &Session<'_>,
358 ) -> Option<Ty> {
359     // temporary fix for https://github.com/rust-lang-nursery/rls/issues/783
360     if !structmatch.mtype.is_struct() {
361         warn!(
362             "get_struct_filed_type is called for {:?}",
363             structmatch.mtype
364         );
365         return None;
366     }
367     debug!("[get_struct_filed_type]{}, {:?}", fieldname, structmatch);
368 
369     let src = session.load_source_file(&structmatch.filepath);
370 
371     let opoint = scopes::expect_stmt_start(src.as_src(), structmatch.point);
372     // HACK: if scopes::end_of_next_scope returns empty struct, it's maybe tuple struct
373     let structsrc = if let Some(end) = scopes::end_of_next_scope(&src[opoint.0..]) {
374         src[opoint.0..=(opoint + end).0].to_owned()
375     } else {
376         (*get_first_stmt(src.as_src().shift_start(opoint))).to_owned()
377     };
378     let fields = ast::parse_struct_fields(structsrc.to_owned(), Scope::from_match(structmatch));
379     for (field, _, ty) in fields {
380         if fieldname != field {
381             continue;
382         }
383         return ty;
384     }
385     None
386 }
387 
get_tuplestruct_fields( structmatch: &Match, session: &Session<'_>, ) -> Vec<(String, ByteRange, Option<Ty>)>388 pub(crate) fn get_tuplestruct_fields(
389     structmatch: &Match,
390     session: &Session<'_>,
391 ) -> Vec<(String, ByteRange, Option<Ty>)> {
392     let src = session.load_source_file(&structmatch.filepath);
393     let structsrc = if let core::MatchType::EnumVariant(_) = structmatch.mtype {
394         // decorate the enum variant src to make it look like a tuple struct
395         let to = src[structmatch.point.0..]
396             .find('(')
397             .map(|n| {
398                 scopes::find_closing_paren(&src, structmatch.point + BytePos::from(n).increment())
399             })
400             .expect("Tuple enum variant should have `(` in definition");
401         "struct ".to_owned() + &src[structmatch.point.0..to.increment().0] + ";"
402     } else {
403         assert!(structmatch.mtype.is_struct());
404         let opoint = scopes::expect_stmt_start(src.as_src(), structmatch.point);
405         (*get_first_stmt(src.as_src().shift_start(opoint))).to_owned()
406     };
407 
408     debug!("[tuplestruct_fields] structsrc=|{}|", structsrc);
409 
410     ast::parse_struct_fields(structsrc, Scope::from_match(structmatch))
411 }
412 
get_tuplestruct_field_type( fieldnum: usize, structmatch: &Match, session: &Session<'_>, ) -> Option<Ty>413 pub fn get_tuplestruct_field_type(
414     fieldnum: usize,
415     structmatch: &Match,
416     session: &Session<'_>,
417 ) -> Option<Ty> {
418     let fields = get_tuplestruct_fields(structmatch, session);
419 
420     for (i, (_, _, ty)) in fields.into_iter().enumerate() {
421         if i == fieldnum {
422             return ty;
423         }
424     }
425     None
426 }
427 
get_first_stmt(src: Src<'_>) -> Src<'_>428 pub fn get_first_stmt(src: Src<'_>) -> Src<'_> {
429     match src.iter_stmts().next() {
430         Some(range) => src.shift_range(range),
431         None => src,
432     }
433 }
434 
get_type_of_match(m: Match, msrc: Src<'_>, session: &Session<'_>) -> Option<Ty>435 pub fn get_type_of_match(m: Match, msrc: Src<'_>, session: &Session<'_>) -> Option<Ty> {
436     debug!("get_type_of match {:?} ", m);
437 
438     match m.mtype {
439         core::MatchType::Let(_) => get_type_of_let_expr(m, session),
440         core::MatchType::IfLet(start) | core::MatchType::WhileLet(start) => {
441             get_type_of_if_let(&m, session, start)
442         }
443         core::MatchType::For(_) => get_type_of_for_arg(&m, session),
444         core::MatchType::FnArg(_) => get_type_of_fnarg(m, session),
445         core::MatchType::MatchArm => get_type_from_match_arm(&m, msrc, session),
446         core::MatchType::Struct(_)
447         | core::MatchType::Union(_)
448         | core::MatchType::Enum(_)
449         | core::MatchType::Function
450         | core::MatchType::Method(_)
451         | core::MatchType::Module => Some(Ty::Match(m)),
452         core::MatchType::Const | core::MatchType::Static => get_type_of_static(m),
453         core::MatchType::EnumVariant(Some(boxed_enum)) => {
454             if boxed_enum.mtype.is_enum() {
455                 Some(Ty::Match(*boxed_enum))
456             } else {
457                 debug!("EnumVariant has not-enum type: {:?}", boxed_enum.mtype);
458                 None
459             }
460         }
461         _ => {
462             debug!("!!! WARNING !!! Can't get type of {:?}", m.mtype);
463             None
464         }
465     }
466 }
467 
get_type_from_match_arm(m: &Match, msrc: Src<'_>, session: &Session<'_>) -> Option<Ty>468 pub fn get_type_from_match_arm(m: &Match, msrc: Src<'_>, session: &Session<'_>) -> Option<Ty> {
469     // We construct a faux match stmt and then parse it. This is because the
470     // match stmt may be incomplete (half written) in the real code
471 
472     // skip to end of match arm pattern so we can search backwards
473     let arm = BytePos(msrc[m.point.0..].find("=>")?) + m.point;
474     let scopestart = scopes::scope_start(msrc, arm);
475 
476     let stmtstart = scopes::find_stmt_start(msrc, scopestart.decrement())?;
477     debug!("PHIL preblock is {:?} {:?}", stmtstart, scopestart);
478     let preblock = &msrc[stmtstart.0..scopestart.0];
479     let matchstart = stmtstart + preblock.rfind("match ")?.into();
480 
481     let lhs_start = scopes::get_start_of_pattern(&msrc, arm);
482     let lhs = &msrc[lhs_start.0..arm.0];
483     // construct faux match statement and recreate point
484     let mut fauxmatchstmt = msrc[matchstart.0..scopestart.0].to_owned();
485     let faux_prefix_size = BytePos::from(fauxmatchstmt.len());
486     fauxmatchstmt = fauxmatchstmt + lhs + " => () };";
487     let faux_point = faux_prefix_size + (m.point - lhs_start);
488 
489     debug!(
490         "fauxmatchstmt for parsing is pt:{:?} src:|{}|",
491         faux_point, fauxmatchstmt
492     );
493 
494     ast::get_match_arm_type(
495         fauxmatchstmt,
496         faux_point,
497         // scope is used to locate expression, so send
498         // it the start of the match expr
499         Scope {
500             filepath: m.filepath.clone(),
501             point: matchstart,
502         },
503         session,
504     )
505 }
506 
get_function_declaration(fnmatch: &Match, session: &Session<'_>) -> String507 pub fn get_function_declaration(fnmatch: &Match, session: &Session<'_>) -> String {
508     let src = session.load_source_file(&fnmatch.filepath);
509     let start = scopes::expect_stmt_start(src.as_src(), fnmatch.point);
510     let def_end: &[_] = &['{', ';'];
511     let end = src[start.0..]
512         .find(def_end)
513         .expect("Definition should have an end (`{` or `;`)");
514     src[start.0..start.0 + end].to_owned()
515 }
516 
get_return_type_of_function( fnmatch: &Match, contextm: &Match, session: &Session<'_>, ) -> Option<Ty>517 pub fn get_return_type_of_function(
518     fnmatch: &Match,
519     contextm: &Match,
520     session: &Session<'_>,
521 ) -> Option<Ty> {
522     let src = session.load_source_file(&fnmatch.filepath);
523     let point = scopes::expect_stmt_start(src.as_src(), fnmatch.point);
524     let block_start = src[point.0..].find('{')?;
525     let decl = "impl b{".to_string() + &src[point.0..point.0 + block_start + 1] + "}}";
526     debug!("get_return_type_of_function: passing in |{}|", decl);
527     let mut scope = Scope::from_match(fnmatch);
528     // TODO(kngwyu): if point <= 5 scope is incorrect
529     scope.point = point.checked_sub("impl b{".len()).unwrap_or(BytePos::ZERO);
530     let (ty, is_async) = ast::parse_fn_output(decl, scope);
531     let resolve_ty = |ty| {
532         if let Some(Ty::PathSearch(ref paths)) = ty {
533             let path = &paths.path;
534             if let Some(ref path_seg) = path.segments.get(0) {
535                 if "Self" == path_seg.name {
536                     return get_type_of_self_arg(fnmatch, src.as_src(), session);
537                 }
538                 if path.segments.len() == 1 && path_seg.generics.is_empty() {
539                     for type_param in fnmatch.generics() {
540                         if type_param.name() == &path_seg.name {
541                             return Some(Ty::Match(contextm.clone()));
542                         }
543                     }
544                 }
545             }
546         }
547         ty
548     };
549     resolve_ty(ty).map(|ty| {
550         if is_async {
551             Ty::Future(Box::new(ty), Scope::from_match(fnmatch))
552         } else {
553             ty
554         }
555     })
556 }
557 
get_type_of_indexed_value(body: Ty, session: &Session<'_>) -> Option<Ty>558 pub(crate) fn get_type_of_indexed_value(body: Ty, session: &Session<'_>) -> Option<Ty> {
559     match body.dereference() {
560         Ty::Match(m) => nameres::get_index_output(&m, session),
561         Ty::PathSearch(p) => p
562             .resolve_as_match(session)
563             .and_then(|m| nameres::get_index_output(&m, session)),
564         Ty::Array(ty, _) | Ty::Slice(ty) => Some(*ty),
565         _ => None,
566     }
567 }
568 
get_type_of_typedef(m: &Match, session: &Session<'_>) -> Option<Match>569 pub(crate) fn get_type_of_typedef(m: &Match, session: &Session<'_>) -> Option<Match> {
570     debug!("get_type_of_typedef match is {:?}", m);
571     let msrc = session.load_source_file(&m.filepath);
572     let blobstart = m.point - BytePos(5); // 5 == "type ".len()
573     let blob = msrc.get_src_from_start(blobstart);
574     let type_ = blob.iter_stmts().nth(0).and_then(|range| {
575         let range = range.shift(blobstart);
576         let blob = msrc[range.to_range()].to_owned();
577         debug!("get_type_of_typedef blob string {}", blob);
578         let scope = Scope::new(m.filepath.clone(), range.start);
579         ast::parse_type(blob, &scope).type_
580     })?;
581     match type_.dereference() {
582         Ty::Match(m) => Some(m),
583         Ty::Ptr(_, _) => PrimKind::Pointer.to_module_match(),
584         Ty::Array(_, _) => PrimKind::Array.to_module_match(),
585         Ty::Slice(_) => PrimKind::Slice.to_module_match(),
586         Ty::PathSearch(paths) => {
587             let src = session.load_source_file(&m.filepath);
588             let scope_start = scopes::scope_start(src.as_src(), m.point);
589             // Type of TypeDef cannot be inside the impl block so look outside
590             let outer_scope_start = scope_start
591                 .0
592                 .checked_sub(1)
593                 .map(|sub| scopes::scope_start(src.as_src(), sub.into()))
594                 .and_then(|s| {
595                     let blob = src.get_src_from_start(s);
596                     let blob = blob.trim_start();
597                     if blob.starts_with("impl") || util::trim_visibility(blob).starts_with("trait")
598                     {
599                         Some(s)
600                     } else {
601                         None
602                     }
603                 });
604             nameres::resolve_path_with_primitive(
605                 &paths.path,
606                 &paths.filepath,
607                 outer_scope_start.unwrap_or(scope_start),
608                 core::SearchType::StartsWith,
609                 core::Namespace::Type,
610                 session,
611             )
612             .into_iter()
613             .filter(|m_| Some(m_.matchstr.as_ref()) == paths.path.name() && m_.point != m.point)
614             .next()
615         }
616         _ => None,
617     }
618 }
619 
get_type_of_static(m: Match) -> Option<Ty>620 fn get_type_of_static(m: Match) -> Option<Ty> {
621     let Match {
622         filepath,
623         point,
624         contextstr,
625         ..
626     } = m;
627     let scope = Scope::new(filepath, point - "static".len().into());
628     let res = ast::parse_static(contextstr, scope);
629     res.ty
630 }
631