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}