go_types/check/resolver.rs
1// Copyright 2022 The Goscript Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4//
5//
6// This code is adapted from the offical Go code written in Go
7// with license as follows:
8// Copyright 2013 The Go Authors. All rights reserved.
9// Use of this source code is governed by a BSD-style
10// license that can be found in the LICENSE file.
11
12#![allow(dead_code)]
13use crate::SourceRead;
14
15use super::super::constant;
16use super::super::importer::ImportKey;
17use super::super::obj::EntityType;
18use super::super::objects::{DeclInfoKey, ObjKey, PackageKey, ScopeKey};
19use super::check::{Checker, FilesContext};
20use go_parser::ast::{self, Expr, Node};
21use go_parser::{FuncDeclKey, IdentKey, Pos, Token};
22use std::collections::HashSet;
23
24#[derive(Debug)]
25pub struct DeclInfoConst {
26 pub file_scope: ScopeKey, // scope of file containing this declaration
27 pub typ: Option<Expr>, // type, or None
28 pub init: Option<Expr>, // init/orig expression, or None
29 pub deps: HashSet<ObjKey>, // deps tracks initialization expression dependencies.
30}
31
32#[derive(Debug)]
33pub struct DeclInfoVar {
34 pub file_scope: ScopeKey, // scope of file containing this declaration
35 pub lhs: Option<Vec<ObjKey>>, // lhs of n:1 variable declarations, or None
36 pub typ: Option<Expr>, // type, or None
37 pub init: Option<Expr>, // init/orig expression, or None
38 pub deps: HashSet<ObjKey>, // deps tracks initialization expression dependencies.
39}
40
41#[derive(Debug)]
42pub struct DeclInfoType {
43 pub file_scope: ScopeKey, // scope of file containing this declaration
44 pub typ: Expr, // type
45 pub alias: bool, // type alias declaration
46}
47
48#[derive(Debug)]
49pub struct DeclInfoFunc {
50 pub file_scope: ScopeKey, // scope of file containing this declaration
51 pub fdecl: FuncDeclKey, // func declaration, or None
52 pub deps: HashSet<ObjKey>, // deps tracks initialization expression dependencies.
53}
54
55/// DeclInfo describes a package-level const, type, var, or func declaration.
56#[derive(Debug)]
57pub enum DeclInfo {
58 Const(DeclInfoConst),
59 Var(DeclInfoVar),
60 Type(DeclInfoType),
61 Func(DeclInfoFunc),
62}
63
64impl DeclInfo {
65 pub fn new_const(file_scope: ScopeKey, typ: Option<Expr>, init: Option<Expr>) -> DeclInfo {
66 DeclInfo::Const(DeclInfoConst {
67 file_scope: file_scope,
68 typ: typ,
69 init: init,
70 deps: HashSet::new(),
71 })
72 }
73
74 pub fn new_var(
75 file_scope: ScopeKey,
76 lhs: Option<Vec<ObjKey>>,
77 typ: Option<Expr>,
78 init: Option<Expr>,
79 ) -> DeclInfo {
80 DeclInfo::Var(DeclInfoVar {
81 file_scope: file_scope,
82 lhs: lhs,
83 typ: typ,
84 init: init,
85 deps: HashSet::new(),
86 })
87 }
88
89 pub fn new_type(file_scope: ScopeKey, typ: Expr, alias: bool) -> DeclInfo {
90 DeclInfo::Type(DeclInfoType {
91 file_scope: file_scope,
92 typ: typ,
93 alias: alias,
94 })
95 }
96
97 pub fn new_func(file_scope: ScopeKey, fdecl: FuncDeclKey) -> DeclInfo {
98 DeclInfo::Func(DeclInfoFunc {
99 file_scope: file_scope,
100 fdecl: fdecl,
101 deps: HashSet::new(),
102 })
103 }
104
105 pub fn as_const(&self) -> &DeclInfoConst {
106 match self {
107 DeclInfo::Const(c) => c,
108 _ => unreachable!(),
109 }
110 }
111
112 pub fn as_var(&self) -> &DeclInfoVar {
113 match self {
114 DeclInfo::Var(v) => v,
115 _ => unreachable!(),
116 }
117 }
118
119 pub fn as_type(&self) -> &DeclInfoType {
120 match self {
121 DeclInfo::Type(t) => t,
122 _ => unreachable!(),
123 }
124 }
125
126 pub fn as_func(&self) -> &DeclInfoFunc {
127 match self {
128 DeclInfo::Func(f) => f,
129 _ => unreachable!(),
130 }
131 }
132
133 pub fn file_scope(&self) -> &ScopeKey {
134 match self {
135 DeclInfo::Const(c) => &c.file_scope,
136 DeclInfo::Var(v) => &v.file_scope,
137 DeclInfo::Type(t) => &t.file_scope,
138 DeclInfo::Func(f) => &f.file_scope,
139 }
140 }
141
142 pub fn deps(&self) -> &HashSet<ObjKey> {
143 match self {
144 DeclInfo::Const(c) => &c.deps,
145 DeclInfo::Var(v) => &v.deps,
146 DeclInfo::Func(f) => &f.deps,
147 _ => unreachable!(),
148 }
149 }
150
151 pub fn deps_mut(&mut self) -> &mut HashSet<ObjKey> {
152 match self {
153 DeclInfo::Const(c) => &mut c.deps,
154 DeclInfo::Var(v) => &mut v.deps,
155 DeclInfo::Func(f) => &mut f.deps,
156 _ => unreachable!(),
157 }
158 }
159
160 pub fn add_dep(&mut self, okey: ObjKey) {
161 self.deps_mut().insert(okey);
162 }
163}
164
165impl<'a, S: SourceRead> Checker<'a, S> {
166 pub fn collect_objects(&mut self, fctx: &mut FilesContext<S>) {
167 let mut all_imported: HashSet<PackageKey> = self
168 .package(self.pkg)
169 .imports()
170 .iter()
171 .map(|x| *x)
172 .collect();
173 // list of methods with non-blank names
174 let mut methods: Vec<ObjKey> = Vec::new();
175 for (file_num, file) in fctx.files.iter().enumerate() {
176 // the original go version record a none here, what for?
177 //self.result_mut().result.(file.name, None)
178
179 // Use the actual source file extent rather than ast::File extent since the
180 // latter doesn't include comments which appear at the start or end of the file.
181 // Be conservative and use the ast::File extent if we don't have a position::File.
182 let mut pos = file.pos(self.ast_objs);
183 let mut end = file.end(self.ast_objs);
184 if let Some(f) = self.fset.file(pos) {
185 pos = f.base();
186 end = pos + f.size();
187 }
188 let parent_scope = Some(*self.package(self.pkg).scope());
189 let scope_comment = fctx.file_name(file_num, self);
190 let file_scope = self
191 .tc_objs
192 .new_scope(parent_scope, pos, end, scope_comment, false);
193 self.result.record_scope(file, file_scope);
194
195 for decl in file.decls.iter() {
196 match decl {
197 ast::Decl::Bad(_) => {}
198 ast::Decl::Gen(gdecl) => {
199 let mut last_full_const_spec: Option<ast::Spec> = None;
200 let specs = &(*gdecl).specs;
201 for (iota, spec_key) in specs.iter().enumerate() {
202 let spec = &self.ast_objs.specs[*spec_key].clone();
203 let spec_pos = spec.pos(self.ast_objs);
204 match spec {
205 ast::Spec::Import(is) => {
206 let ispec = &**is;
207 let path = match self.valid_import_path(&ispec.path) {
208 Ok(p) => p,
209 Err(e) => {
210 self.error(
211 ispec.path.pos,
212 format!("invalid import path ({})", e),
213 );
214 continue;
215 }
216 };
217 let dir = self.file_dir(file);
218 let imp =
219 self.import_package(ispec.path.pos, path.to_string(), dir);
220
221 // add package to list of explicit imports
222 // (this functionality is provided as a convenience
223 // for clients; it is not needed for type-checking)
224 if !all_imported.contains(&imp) {
225 all_imported.insert(imp);
226 self.package_mut(self.pkg).add_import(imp);
227 }
228
229 let name = ispec.name.map_or(
230 self.package(imp).name().clone().unwrap(),
231 |x| {
232 // see if local name overrides imported package name
233 let ident = &self.ast_ident(x);
234 if ident.name == "init" {
235 self.error_str(
236 ident.pos,
237 "cannot declare init - must be func",
238 );
239 }
240 ident.name.clone()
241 },
242 );
243
244 let pkg_name_obj = self.tc_objs.new_pkg_name(
245 spec_pos,
246 Some(self.pkg),
247 name.to_owned(),
248 imp,
249 );
250 if let Some(n) = ispec.name {
251 // in a dot-import, the dot represents the package
252 self.result.record_def(n, Some(pkg_name_obj));
253 } else {
254 self.result.record_implicit(spec, pkg_name_obj);
255 }
256
257 // add import to file scope
258 if name == "." {
259 // merge imported scope with file scope
260 let pkg_val = self.package(imp);
261 let scope_val = self.scope(*pkg_val.scope());
262 let elems: Vec<ObjKey> = scope_val
263 .elems()
264 .iter()
265 .filter_map(|(_, v)| {
266 if self.lobj(*v).exported() {
267 Some(*v)
268 } else {
269 None
270 }
271 })
272 .collect();
273 for elem in elems.into_iter() {
274 self.declare(file_scope, None, elem, 0);
275 }
276 // add position to set of dot-import positions for this file
277 // (this is only needed for "imported but not used" errors)
278 fctx.add_unused_dot_import(&file_scope, &imp, spec_pos);
279 } else {
280 // declare imported package object in file scope
281 self.declare(file_scope, None, pkg_name_obj, 0);
282 }
283 }
284 ast::Spec::Value(vs) => {
285 let vspec = &**vs;
286 match gdecl.token {
287 Token::CONST => {
288 let mut current_vspec = None;
289 if vspec.typ.is_some() || vspec.values.len() > 0 {
290 last_full_const_spec = Some(spec.clone());
291 current_vspec = Some(vspec);
292 } else {
293 // no ValueSpec with type or init exprs,
294 // try get the last one
295 if let Some(spec) = &last_full_const_spec {
296 match spec {
297 ast::Spec::Value(v) => {
298 current_vspec = Some(&*v);
299 }
300 _ => unreachable!(),
301 }
302 }
303 }
304 // declare all constants
305 for (i, name) in
306 vspec.names.clone().into_iter().enumerate()
307 {
308 let ident = &self.ast_objs.idents[name];
309 let okey = self.tc_objs.new_const(
310 ident.pos,
311 Some(self.pkg),
312 ident.name.clone(),
313 None,
314 constant::Value::with_i64(iota as i64),
315 );
316 let init = if current_vspec.is_some()
317 && i < current_vspec.unwrap().values.len()
318 {
319 Some(current_vspec.unwrap().values[i].clone())
320 } else {
321 None
322 };
323 let typ =
324 current_vspec.map(|x| x.typ.clone()).flatten();
325 let d = self.tc_objs.decls.insert(
326 DeclInfo::new_const(file_scope, typ, init),
327 );
328 let _ = self.declare_pkg_obj(name, okey, d);
329 }
330 self.arity_match(vspec, true, current_vspec);
331 }
332 Token::VAR => {
333 let lhs: Vec<ObjKey> = vspec
334 .names
335 .iter()
336 .map(|x| {
337 let ident = &self.ast_objs.idents[*x];
338 self.tc_objs.new_var(
339 ident.pos,
340 Some(self.pkg),
341 ident.name.clone(),
342 None,
343 )
344 })
345 .collect();
346 let n_to_1 =
347 vspec.values.len() == 1 && vspec.names.len() > 1;
348 let n_to_1_di = if n_to_1 {
349 Some(self.tc_objs.decls.insert(DeclInfo::new_var(
350 file_scope,
351 Some(lhs.clone()),
352 vspec.typ.clone(),
353 Some(vspec.values[0].clone()),
354 )))
355 } else {
356 None
357 };
358 for (i, name) in vspec.names.iter().enumerate() {
359 let di = if n_to_1 {
360 n_to_1_di.unwrap()
361 } else {
362 self.tc_objs.decls.insert(DeclInfo::new_var(
363 file_scope,
364 None,
365 vspec.typ.clone(),
366 vspec.values.get(i).map(|x| x.clone()),
367 ))
368 };
369 let _ = self.declare_pkg_obj(*name, lhs[i], di);
370 }
371
372 self.arity_match(vspec, false, None);
373 }
374 _ => self.error(
375 spec_pos,
376 format!("invalid token {}", gdecl.token),
377 ),
378 }
379 }
380 ast::Spec::Type(ts) => {
381 let tspec = &**ts;
382 let ident = &self.ast_objs.idents[tspec.name];
383 let okey = self.tc_objs.new_type_name(
384 ident.pos,
385 Some(self.pkg),
386 ident.name.clone(),
387 None,
388 );
389 let di = self.tc_objs.decls.insert(DeclInfo::new_type(
390 file_scope,
391 tspec.typ.clone(),
392 tspec.assign > 0,
393 ));
394 let _ = self.declare_pkg_obj(tspec.name, okey, di);
395 }
396 }
397 }
398 }
399 ast::Decl::Func(fdkey) => {
400 let fdecl = &self.ast_objs.fdecls[*fdkey];
401 let ident_key = fdecl.name;
402 let ident = &self.ast_objs.idents[ident_key];
403 let lobj = self.tc_objs.new_func(
404 ident.pos,
405 Some(self.pkg),
406 ident.name.clone(),
407 None,
408 );
409 if fdecl.recv.is_none() {
410 // regular function
411 let scope = *self.package(self.pkg).scope();
412 if ident.name == "init" {
413 self.tc_objs.lobjs[lobj].set_parent(Some(scope));
414 self.result.record_def(ident_key, Some(lobj));
415 if fdecl.body.is_none() {
416 self.error(ident.pos, "missing function body".to_owned());
417 }
418 } else {
419 self.declare(scope, Some(ident_key), lobj, 0);
420 }
421 } else {
422 // method
423 // (Methods with blank _ names are never found; no need to collect
424 // them for later type association. They will still be type-checked
425 // with all the other functions.)
426 if ident.name != "_" {
427 methods.push(lobj);
428 }
429 self.result.record_def(ident_key, Some(lobj));
430 }
431 let di = self
432 .tc_objs
433 .decls
434 .insert(DeclInfo::new_func(file_scope, *fdkey));
435 self.obj_map.insert(lobj, di);
436 let order = self.obj_map.len() as u32;
437 self.lobj_mut(lobj).set_order(order);
438 }
439 }
440 }
441 }
442 // verify that objects in package and file scopes have different names
443 let pkg_scope = self.scope(*self.package(self.pkg).scope());
444 for s in pkg_scope.children().iter() {
445 for (_, okey) in self.scope(*s).elems() {
446 let obj_val = self.lobj(*okey);
447 if let Some(alt) = pkg_scope.lookup(obj_val.name()) {
448 let alt_val = self.lobj(*alt);
449 match obj_val.entity_type() {
450 EntityType::PkgName(pkey, _) => {
451 let pkg_val = self.package(*pkey);
452 self.error(
453 alt_val.pos(),
454 format!(
455 "{} already declared through import of {}",
456 alt_val.name(),
457 pkg_val
458 ),
459 );
460 }
461 _ => {
462 let pkg_val = self.package(obj_val.pkg().unwrap());
463 self.error(
464 alt_val.pos(),
465 format!(
466 "{} already declared through dot-import of {}",
467 alt_val.name(),
468 pkg_val
469 ),
470 );
471 }
472 }
473 self.report_alt_decl(*okey);
474 }
475 }
476 }
477 // Now that we have all package scope objects and all methods,
478 // associate methods with receiver base type name where possible.
479 // Ignore methods that have an invalid receiver. They will be
480 // type-checked later, with regular functions.
481 for f in methods.into_iter() {
482 let fdkey = self.tc_objs.decls[self.obj_map[&f]].as_func().fdecl;
483 let fdecl = &self.ast_objs.fdecls[fdkey];
484 if let Some(fl) = &fdecl.recv {
485 // f is a method.
486 // determine the receiver base type and associate f with it.
487 let typ = &self.ast_objs.fields[fl.list[0]].typ;
488 if let Some((ptr, base)) = self.resolve_base_type_name(typ) {
489 self.lobj_mut(f)
490 .entity_type_mut()
491 .func_set_has_ptr_recv(ptr);
492 fctx.methods.entry(base).or_default().push(f);
493 }
494 }
495 }
496 }
497
498 /// package_objects typechecks all package objects, but not function bodies.
499 pub fn package_objects(&mut self, fctx: &mut FilesContext<S>) {
500 // process package objects in source order for reproducible results
501 let mut obj_list: Vec<ObjKey> = self.obj_map.iter().map(|(o, _)| *o).collect();
502 obj_list.sort_by(|a, b| self.lobj(*a).order().cmp(&self.lobj(*b).order()));
503
504 for o in obj_list.iter() {
505 let lobj = self.lobj(*o);
506 if lobj.entity_type().is_type_name() && lobj.typ().is_some() {
507 self.add_method_decls(*o, fctx);
508 }
509 }
510
511 // We process non-alias declarations first, in order to avoid situations where
512 // the type of an alias declaration is needed before it is available. In general
513 // this is still not enough, as it is possible to create sufficiently convoluted
514 // recursive type definitions that will cause a type alias to be needed before it
515 // is available (see Golang issue #25838 for examples).
516 // As an aside, the cmd/compiler suffers from the same problem (Golang #25838).
517 let alias_list: Vec<ObjKey> = obj_list
518 .into_iter()
519 .filter(|&o| {
520 if self.lobj(o).entity_type().is_type_name()
521 && self.decl_info(self.obj_map[&o]).as_type().alias
522 {
523 true
524 } else {
525 // phase 1
526 self.obj_decl(o, None, fctx);
527 false
528 }
529 })
530 .collect();
531 for o in alias_list.into_iter() {
532 // phase 2
533 self.obj_decl(o, None, fctx);
534 }
535
536 // At this point we may have a non-empty FilesContext.methods map; this means that
537 // not all entries were deleted at the end of type_decl because the respective
538 // receiver base types were not found. In that case, an error was reported when
539 // declaring those methods. We can now safely discard this map.
540 fctx.methods.clear();
541 }
542
543 /// unused_imports checks for unused imports.
544 pub fn unused_imports(&mut self, fctx: &mut FilesContext<S>) {
545 // check use of regular imported packages
546 let pkg_scope = self.scope(*self.package(self.pkg).scope());
547 for s in pkg_scope.children().iter() {
548 for (_, okey) in self.scope(*s).elems() {
549 let obj_val = self.lobj(*okey);
550 match obj_val.entity_type() {
551 EntityType::PkgName(pkey, used) => {
552 if !*used {
553 let (path, base) = self.pkg_path_and_name(*pkey);
554 if obj_val.name() == base {
555 self.soft_error(
556 obj_val.pos(),
557 format!("{} imported but not used", path),
558 );
559 } else {
560 self.soft_error(
561 obj_val.pos(),
562 format!("{} imported but not used as {}", path, base),
563 );
564 }
565 }
566 }
567 _ => {}
568 }
569 }
570 }
571 // check use of dot-imported packages
572 for (_, imports) in fctx.unused_dot_imports.iter() {
573 for (pkey, pos) in imports.iter() {
574 self.soft_error(
575 *pos,
576 format!("{} imported but not used", self.package(*pkey).path()),
577 );
578 }
579 }
580 }
581
582 /// arity_match checks that the lhs and rhs of a const or var decl
583 /// have the appropriate number of names and init exprs.
584 /// set 'cst' as true for const decls, 'init' is not used for var decls.
585 pub fn arity_match(&self, s: &ast::ValueSpec, cst: bool, init: Option<&ast::ValueSpec>) {
586 let l = s.names.len();
587 let r = if cst {
588 if let Some(i) = init {
589 i.values.len()
590 } else {
591 0
592 }
593 } else {
594 s.values.len()
595 };
596 if !cst && r == 0 {
597 // var decl w/o init expr
598 if s.typ.is_none() {
599 self.error(
600 self.ast_ident(s.names[0]).pos,
601 "missing type or init expr".to_owned(),
602 );
603 }
604 } else if l < r {
605 if l < s.values.len() {
606 let expr = &s.values[l];
607 let ed = self.new_dis(expr);
608 self.error(ed.pos(), format!("extra init expr {}", ed));
609 } else {
610 let pos = self.ast_ident(init.unwrap().names[0]).pos;
611 self.error(
612 self.ast_ident(s.names[0]).pos,
613 format!("extra init expr at {}", self.position(pos)),
614 );
615 }
616 } else if l > r && (cst || r != 1) {
617 let ident = self.ast_ident(s.names[r]);
618 self.error(ident.pos, format!("missing init expr for {}", ident.name));
619 }
620 }
621
622 // resolve_base_type_name returns the non-alias base type name for typ, and whether
623 // there was a pointer indirection to get to it. The base type name must be declared
624 // in package scope, and there can be at most one pointer indirection. If no such type
625 // name exists, the returned base is nil.
626 // Algorithm: Starting from a type expression, which may be a name,
627 // we follow that type through alias declarations until we reach a
628 // non-alias type name. If we encounter anything but pointer types or
629 // parentheses we're done. If we encounter more than one pointer type
630 // we're done.
631 fn resolve_base_type_name(&self, expr: &Expr) -> Option<(bool, ObjKey)> {
632 let scope = self.scope(*self.package(self.pkg).scope());
633 let mut typ = expr;
634 let mut path = Vec::new();
635 let mut ptr = false;
636 loop {
637 typ = Checker::<S>::unparen(typ);
638 if let Expr::Star(t) = typ {
639 // if we've already seen a pointer, we're done
640 if ptr {
641 break;
642 }
643 ptr = true;
644 typ = Checker::<S>::unparen(&t.expr);
645 }
646
647 // typ must be the name
648 if let Expr::Ident(i) = typ {
649 // name must denote an object found in the current package scope
650 // (note that dot-imported objects are not in the package scope!)
651 let ident = &self.ast_objs.idents[*i];
652 if let Some(&okey) = scope.lookup(&ident.name) {
653 let lobj = self.lobj(okey);
654 // the object must be a type name...
655 if !lobj.entity_type().is_type_name() {
656 break;
657 }
658 // ... which we have not seen before
659 if self.has_cycle(okey, &path, false) {
660 break;
661 }
662 if let DeclInfo::Type(t) = &self.tc_objs.decls[self.obj_map[&okey]] {
663 if !t.alias {
664 // we're done if tdecl defined tname as a new type
665 // (rather than an alias)
666 return Some((ptr, okey));
667 } else {
668 // otherwise, continue resolving
669 typ = &t.typ;
670 path.push(okey);
671 continue;
672 }
673 }
674 }
675 }
676 break;
677 }
678 None
679 }
680
681 fn valid_import_path(&self, blit: &'a ast::BasicLit) -> Result<&'a str, String> {
682 let path = blit.token.get_literal();
683 if path.len() < 3 || (!path.starts_with('"') || !path.ends_with('"')) {
684 return Err("empty string".to_owned());
685 }
686 let result = &path[1..path.len() - 1];
687 let mut illegal_chars: Vec<char> = r##"!"#$%&'()*,:;<=>?[\]^{|}`"##.chars().collect();
688 illegal_chars.push('\u{FFFD}');
689 if let Some(c) = result
690 .chars()
691 .find(|&x| !x.is_ascii_graphic() || x.is_whitespace() || illegal_chars.contains(&x))
692 {
693 return Err(format!("invalid character: {}", c));
694 }
695 Ok(result)
696 }
697
698 /// declare_pkg_obj declares obj in the package scope, records its ident -> obj mapping,
699 /// and updates check.objMap. The object must not be a function or method.
700 fn declare_pkg_obj(
701 &mut self,
702 ikey: IdentKey,
703 okey: ObjKey,
704 dkey: DeclInfoKey,
705 ) -> Result<(), ()> {
706 let ident = self.ast_ident(ikey);
707 let lobj = self.lobj(okey);
708 assert_eq!(&ident.name, lobj.name());
709 // spec: "A package-scope or file-scope identifier with name init
710 // may only be declared to be a function with this (func()) signature."
711 if &ident.name == "init" {
712 self.error_str(ident.pos, "cannot declare init - must be func");
713 return Err(());
714 }
715 // spec: "The main package must have package name main and declare
716 // a function main that takes no arguments and returns no value."
717 let pkg_name = self.package(self.pkg).name();
718 if &ident.name == "main" && pkg_name.is_some() && pkg_name.as_ref().unwrap() == "main" {
719 self.error_str(ident.pos, "cannot declare main - must be func");
720 return Err(());
721 }
722 let scope = *self.package(self.pkg).scope();
723 self.declare(scope, Some(ikey), okey, 0);
724 self.obj_map.insert(okey, dkey);
725 let order = self.obj_map.len() as u32;
726 self.lobj_mut(okey).set_order(order);
727 Ok(())
728 }
729
730 fn import_package(&mut self, pos: Pos, path: String, dir: String) -> PackageKey {
731 // If we already have a package for the given (path, dir)
732 // pair, use it instead of doing a full import.
733 // Checker.imp_map only caches packages that are marked Complete
734 // or fake (dummy packages for failed imports). Incomplete but
735 // non-fake packages do require an import to complete them.
736 let key = ImportKey::new(&path, &dir);
737 if let Some(imp) = self.imp_map.get(&key) {
738 return *imp;
739 }
740
741 let mut imported = self.new_importer(pos).import(&key);
742 if imported.is_err() {
743 self.error(pos, format!("could not import {}", &path));
744 // create a new fake package
745 let mut name = &path[0..path.len()];
746 if name.len() > 0 && name.ends_with('/') {
747 name = &name[0..name.len() - 1];
748 }
749 if let Some(i) = name.rfind('/') {
750 name = &name[i..name.len()]
751 }
752 let pkg = self.tc_objs.new_package(path.clone());
753 self.package_mut(pkg).mark_fake_with_name(name.to_owned());
754 imported = Ok(pkg);
755 }
756 self.imp_map.insert(key, imported.unwrap());
757 imported.unwrap()
758 }
759
760 // pkg_name returns the package's path and name (last element) of a PkgName obj.
761 fn pkg_path_and_name(&self, pkey: PackageKey) -> (&str, &str) {
762 let pkg_val = self.package(pkey);
763 let path = pkg_val.path();
764 if let Some((i, _)) = path.match_indices('/').next() {
765 if i > 0 {
766 return (&path, &path[i + 1..]);
767 }
768 }
769 (&path, &path)
770 }
771
772 /// dir makes a good-faith attempt to return the directory
773 /// portion of path. If path is empty, the result is ".".
774 fn file_dir(&self, file: &ast::File) -> String {
775 let path = self
776 .fset
777 .file(self.ast_ident(file.name).pos)
778 .unwrap()
779 .name();
780 if let Some((i, _)) = path.rmatch_indices(&['/', '\\'][..]).next() {
781 if i > 0 {
782 return path[0..i].to_owned();
783 }
784 }
785 ".".to_owned()
786 }
787}