/* Compiler implementation of the D programming language * Copyright (C) 1999-2021 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(Prot::private_); // 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->length) { // 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.length; 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) { if (mod && mod->isPackageFile) { // one level depth package.d file (import pkg; ./pkg/package.d) // it's necessary to use the wrapping Package already created pkg = mod->pkg; } else pkg = mod; } //printf("-Import::load('%s'), pkg = %p\n", toChars(), pkg); } void Import::importAll(Scope *sc) { if (mod) return; // Already done load(sc); if (!mod) return; // Failed if (sc->stc & STCstatic) isstatic = true; 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.length) sc->scopesym->importScope(mod, protection); // Enable access to pkgs/mod as soon as posible, because compiler // can traverse them before the import gets semantic (Issue: 21501) if (!aliasId && !names.length) addPackageAccess(sc->scopesym); } /******************************* * 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. * https://issues.dlang.org/show_bug.cgi?id=313 */ void Import::addPackageAccess(ScopeDsymbol *scopesym) { //printf("Import::addPackageAccess('%s') %p\n", toPrettyChars(), this); if (packages) { // import a.b.c.d; Package *p = pkg; // a scopesym->addAccessiblePackage(p, protection); for (size_t i = 1; i < packages->length; i++) // [b, c] { Identifier *id = (*packages)[i]; p = (Package *) p->symtab->lookup(id); // https://issues.dlang.org/show_bug.cgi?id=17991 // An import of truly empty file/package can happen // https://issues.dlang.org/show_bug.cgi?id=20151 // Package in the path conflicts with a module name if (p == NULL) return; scopesym->addAccessiblePackage(p, protection); } } scopesym->addAccessiblePackage(mod, protection); // d } 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.length == 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.length; 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.length) { if (!mod) importAll(sc); sc = sc->push(mod); sc->protection = protection; for (size_t i = 0; i < aliasdecls.length; 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); dsymbolSemantic(mod, 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; }