/* 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; }