| |
| /* Compiler implementation of the D programming language |
| * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved |
| * written by Walter Bright |
| * http://www.digitalmars.com |
| * Distributed under the Boost Software License, Version 1.0. |
| * http://www.boost.org/LICENSE_1_0.txt |
| * https://github.com/D-Programming-Language/dmd/blob/master/src/import.c |
| */ |
| |
| #include "root/dsystem.h" |
| #include "root/root.h" |
| |
| #include "mars.h" |
| #include "dsymbol.h" |
| #include "import.h" |
| #include "identifier.h" |
| #include "module.h" |
| #include "scope.h" |
| #include "mtype.h" |
| #include "declaration.h" |
| #include "id.h" |
| #include "attrib.h" |
| #include "hdrgen.h" |
| |
| /********************************* Import ****************************/ |
| |
| Import::Import(Loc loc, Identifiers *packages, Identifier *id, Identifier *aliasId, |
| int isstatic) |
| : Dsymbol(NULL) |
| { |
| assert(id); |
| this->loc = loc; |
| this->packages = packages; |
| this->id = id; |
| this->aliasId = aliasId; |
| this->isstatic = isstatic; |
| this->protection = Prot(PROTprivate); // default to private |
| this->pkg = NULL; |
| this->mod = NULL; |
| |
| // Set symbol name (bracketed) |
| if (aliasId) |
| { |
| // import [cstdio] = std.stdio; |
| this->ident = aliasId; |
| } |
| else if (packages && packages->dim) |
| { |
| // import [std].stdio; |
| this->ident = (*packages)[0]; |
| } |
| else |
| { |
| // import [foo]; |
| this->ident = id; |
| } |
| } |
| |
| void Import::addAlias(Identifier *name, Identifier *alias) |
| { |
| if (isstatic) |
| error("cannot have an import bind list"); |
| |
| if (!aliasId) |
| this->ident = NULL; // make it an anonymous import |
| |
| names.push(name); |
| aliases.push(alias); |
| } |
| |
| const char *Import::kind() const |
| { |
| return isstatic ? "static import" : "import"; |
| } |
| |
| Prot Import::prot() |
| { |
| return protection; |
| } |
| |
| Dsymbol *Import::syntaxCopy(Dsymbol *s) |
| { |
| assert(!s); |
| |
| Import *si = new Import(loc, packages, id, aliasId, isstatic); |
| |
| for (size_t i = 0; i < names.dim; i++) |
| { |
| si->addAlias(names[i], aliases[i]); |
| } |
| |
| return si; |
| } |
| |
| void Import::load(Scope *sc) |
| { |
| //printf("Import::load('%s') %p\n", toPrettyChars(), this); |
| |
| // See if existing module |
| DsymbolTable *dst = Package::resolve(packages, NULL, &pkg); |
| Dsymbol *s = dst->lookup(id); |
| if (s) |
| { |
| if (s->isModule()) |
| mod = (Module *)s; |
| else |
| { |
| if (s->isAliasDeclaration()) |
| { |
| ::error(loc, "%s %s conflicts with %s", s->kind(), s->toPrettyChars(), id->toChars()); |
| } |
| else if (Package *p = s->isPackage()) |
| { |
| if (p->isPkgMod == PKGunknown) |
| { |
| mod = Module::load(loc, packages, id); |
| if (!mod) |
| p->isPkgMod = PKGpackage; |
| else |
| { |
| // mod is a package.d, or a normal module which conflicts with the package name. |
| assert(mod->isPackageFile == (p->isPkgMod == PKGmodule)); |
| if (mod->isPackageFile) |
| mod->tag = p->tag; // reuse the same package tag |
| } |
| } |
| else |
| { |
| mod = p->isPackageMod(); |
| } |
| if (!mod) |
| { |
| ::error(loc, "can only import from a module, not from package %s.%s", |
| p->toPrettyChars(), id->toChars()); |
| } |
| } |
| else if (pkg) |
| { |
| ::error(loc, "can only import from a module, not from package %s.%s", |
| pkg->toPrettyChars(), id->toChars()); |
| } |
| else |
| { |
| ::error(loc, "can only import from a module, not from package %s", |
| id->toChars()); |
| } |
| } |
| } |
| |
| if (!mod) |
| { |
| // Load module |
| mod = Module::load(loc, packages, id); |
| if (mod) |
| { |
| dst->insert(id, mod); // id may be different from mod->ident, |
| // if so then insert alias |
| } |
| } |
| if (mod && !mod->importedFrom) |
| mod->importedFrom = sc ? sc->_module->importedFrom : Module::rootModule; |
| if (!pkg) |
| pkg = mod; |
| |
| //printf("-Import::load('%s'), pkg = %p\n", toChars(), pkg); |
| } |
| |
| void Import::importAll(Scope *sc) |
| { |
| if (!mod) |
| { |
| load(sc); |
| if (mod) // if successfully loaded module |
| { |
| mod->importAll(NULL); |
| |
| if (mod->md && mod->md->isdeprecated) |
| { |
| Expression *msg = mod->md->msg; |
| if (StringExp *se = msg ? msg->toStringExp() : NULL) |
| mod->deprecation(loc, "is deprecated - %s", se->string); |
| else |
| mod->deprecation(loc, "is deprecated"); |
| } |
| |
| if (sc->explicitProtection) |
| protection = sc->protection; |
| if (!isstatic && !aliasId && !names.dim) |
| { |
| sc->scopesym->importScope(mod, protection); |
| } |
| } |
| } |
| } |
| |
| void Import::semantic(Scope *sc) |
| { |
| //printf("Import::semantic('%s') %s\n", toPrettyChars(), id->toChars()); |
| |
| if (_scope) |
| { |
| sc = _scope; |
| _scope = NULL; |
| } |
| |
| // Load if not already done so |
| if (!mod) |
| { |
| load(sc); |
| if (mod) |
| mod->importAll(NULL); |
| } |
| |
| if (mod) |
| { |
| // Modules need a list of each imported module |
| //printf("%s imports %s\n", sc->_module->toChars(), mod->toChars()); |
| sc->_module->aimports.push(mod); |
| |
| if (sc->explicitProtection) |
| protection = sc->protection; |
| |
| if (!aliasId && !names.dim) // neither a selective nor a renamed import |
| { |
| ScopeDsymbol *scopesym = NULL; |
| if (sc->explicitProtection) |
| protection = sc->protection.kind; |
| for (Scope *scd = sc; scd; scd = scd->enclosing) |
| { |
| if (!scd->scopesym) |
| continue; |
| scopesym = scd->scopesym; |
| break; |
| } |
| |
| if (!isstatic) |
| { |
| scopesym->importScope(mod, protection); |
| } |
| |
| // Mark the imported packages as accessible from the current |
| // scope. This access check is necessary when using FQN b/c |
| // we're using a single global package tree. See Bugzilla 313. |
| if (packages) |
| { |
| // import a.b.c.d; |
| Package *p = pkg; // a |
| scopesym->addAccessiblePackage(p, protection); |
| for (size_t i = 1; i < packages->dim; i++) // [b, c] |
| { |
| Identifier *id = (*packages)[i]; |
| p = (Package *) p->symtab->lookup(id); |
| scopesym->addAccessiblePackage(p, protection); |
| } |
| } |
| scopesym->addAccessiblePackage(mod, protection); // d |
| } |
| |
| mod->semantic(NULL); |
| |
| if (mod->needmoduleinfo) |
| { |
| //printf("module4 %s because of %s\n", sc->_module->toChars(), mod->toChars()); |
| sc->_module->needmoduleinfo = 1; |
| } |
| |
| sc = sc->push(mod); |
| sc->protection = protection; |
| for (size_t i = 0; i < aliasdecls.dim; i++) |
| { |
| AliasDeclaration *ad = aliasdecls[i]; |
| //printf("\tImport %s alias %s = %s, scope = %p\n", toPrettyChars(), aliases[i]->toChars(), names[i]->toChars(), ad->_scope); |
| if (mod->search(loc, names[i])) |
| { |
| ad->semantic(sc); |
| // If the import declaration is in non-root module, |
| // analysis of the aliased symbol is deferred. |
| // Therefore, don't see the ad->aliassym or ad->type here. |
| } |
| else |
| { |
| Dsymbol *s = mod->search_correct(names[i]); |
| if (s) |
| mod->error(loc, "import '%s' not found, did you mean %s '%s'?", names[i]->toChars(), s->kind(), s->toChars()); |
| else |
| mod->error(loc, "import '%s' not found", names[i]->toChars()); |
| ad->type = Type::terror; |
| } |
| } |
| sc = sc->pop(); |
| } |
| |
| // object self-imports itself, so skip that (Bugzilla 7547) |
| // don't list pseudo modules __entrypoint.d, __main.d (Bugzilla 11117, 11164) |
| if (global.params.moduleDeps != NULL && |
| !(id == Id::object && sc->_module->ident == Id::object) && |
| sc->_module->ident != Id::entrypoint && |
| strcmp(sc->_module->ident->toChars(), "__main") != 0) |
| { |
| /* The grammar of the file is: |
| * ImportDeclaration |
| * ::= BasicImportDeclaration [ " : " ImportBindList ] [ " -> " |
| * ModuleAliasIdentifier ] "\n" |
| * |
| * BasicImportDeclaration |
| * ::= ModuleFullyQualifiedName " (" FilePath ") : " Protection|"string" |
| * " [ " static" ] : " ModuleFullyQualifiedName " (" FilePath ")" |
| * |
| * FilePath |
| * - any string with '(', ')' and '\' escaped with the '\' character |
| */ |
| |
| OutBuffer *ob = global.params.moduleDeps; |
| Module* imod = sc->instantiatingModule(); |
| if (!global.params.moduleDepsFile) |
| ob->writestring("depsImport "); |
| ob->writestring(imod->toPrettyChars()); |
| ob->writestring(" ("); |
| escapePath(ob, imod->srcfile->toChars()); |
| ob->writestring(") : "); |
| |
| // use protection instead of sc->protection because it couldn't be |
| // resolved yet, see the comment above |
| protectionToBuffer(ob, protection); |
| ob->writeByte(' '); |
| if (isstatic) |
| { |
| stcToBuffer(ob, STCstatic); |
| ob->writeByte(' '); |
| } |
| ob->writestring(": "); |
| |
| if (packages) |
| { |
| for (size_t i = 0; i < packages->dim; i++) |
| { |
| Identifier *pid = (*packages)[i]; |
| ob->printf("%s.", pid->toChars()); |
| } |
| } |
| |
| ob->writestring(id->toChars()); |
| ob->writestring(" ("); |
| if (mod) |
| escapePath(ob, mod->srcfile->toChars()); |
| else |
| ob->writestring("???"); |
| ob->writeByte(')'); |
| |
| for (size_t i = 0; i < names.dim; i++) |
| { |
| if (i == 0) |
| ob->writeByte(':'); |
| else |
| ob->writeByte(','); |
| |
| Identifier *name = names[i]; |
| Identifier *alias = aliases[i]; |
| |
| if (!alias) |
| { |
| ob->printf("%s", name->toChars()); |
| alias = name; |
| } |
| else |
| ob->printf("%s=%s", alias->toChars(), name->toChars()); |
| } |
| |
| if (aliasId) |
| ob->printf(" -> %s", aliasId->toChars()); |
| |
| ob->writenl(); |
| } |
| |
| //printf("-Import::semantic('%s'), pkg = %p\n", toChars(), pkg); |
| } |
| |
| void Import::semantic2(Scope *sc) |
| { |
| //printf("Import::semantic2('%s')\n", toChars()); |
| if (mod) |
| { |
| mod->semantic2(NULL); |
| if (mod->needmoduleinfo) |
| { |
| //printf("module5 %s because of %s\n", sc->_module->toChars(), mod->toChars()); |
| if (sc) |
| sc->_module->needmoduleinfo = 1; |
| } |
| } |
| } |
| |
| Dsymbol *Import::toAlias() |
| { |
| if (aliasId) |
| return mod; |
| return this; |
| } |
| |
| /***************************** |
| * Add import to sd's symbol table. |
| */ |
| |
| void Import::addMember(Scope *sc, ScopeDsymbol *sd) |
| { |
| //printf("Import::addMember(this=%s, sd=%s, sc=%p)\n", toChars(), sd->toChars(), sc); |
| if (names.dim == 0) |
| return Dsymbol::addMember(sc, sd); |
| |
| if (aliasId) |
| Dsymbol::addMember(sc, sd); |
| |
| /* Instead of adding the import to sd's symbol table, |
| * add each of the alias=name pairs |
| */ |
| for (size_t i = 0; i < names.dim; i++) |
| { |
| Identifier *name = names[i]; |
| Identifier *alias = aliases[i]; |
| |
| if (!alias) |
| alias = name; |
| |
| TypeIdentifier *tname = new TypeIdentifier(loc, name); |
| AliasDeclaration *ad = new AliasDeclaration(loc, alias, tname); |
| ad->_import = this; |
| ad->addMember(sc, sd); |
| |
| aliasdecls.push(ad); |
| } |
| } |
| |
| void Import::setScope(Scope *sc) |
| { |
| Dsymbol::setScope(sc); |
| if (aliasdecls.dim) |
| { |
| if (!mod) |
| importAll(sc); |
| |
| sc = sc->push(mod); |
| sc->protection = protection; |
| for (size_t i = 0; i < aliasdecls.dim; i++) |
| { |
| AliasDeclaration *ad = aliasdecls[i]; |
| ad->setScope(sc); |
| } |
| sc = sc->pop(); |
| } |
| } |
| |
| Dsymbol *Import::search(const Loc &loc, Identifier *ident, int flags) |
| { |
| //printf("%s.Import::search(ident = '%s', flags = x%x)\n", toChars(), ident->toChars(), flags); |
| |
| if (!pkg) |
| { |
| load(NULL); |
| mod->importAll(NULL); |
| mod->semantic(NULL); |
| } |
| |
| // Forward it to the package/module |
| return pkg->search(loc, ident, flags); |
| } |
| |
| bool Import::overloadInsert(Dsymbol *s) |
| { |
| /* Allow multiple imports with the same package base, but disallow |
| * alias collisions (Bugzilla 5412). |
| */ |
| assert(ident && ident == s->ident); |
| Import *imp; |
| if (!aliasId && (imp = s->isImport()) != NULL && !imp->aliasId) |
| return true; |
| else |
| return false; |
| } |