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