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}