| /** | |
| * A scope as defined by curly braces `{}`. | |
| * | |
| * Not to be confused with the `scope` storage class. | |
| * | |
| * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved | |
| * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) | |
| * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) | |
| * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dscope.d, _dscope.d) | |
| * Documentation: https://dlang.org/phobos/dmd_dscope.html | |
| * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dscope.d | |
| */ | |
| module dmd.dscope; | |
| import core.stdc.stdio; | |
| import core.stdc.string; | |
| import dmd.aggregate; | |
| import dmd.arraytypes; | |
| import dmd.astenums; | |
| import dmd.attrib; | |
| import dmd.ctorflow; | |
| import dmd.dclass; | |
| import dmd.declaration; | |
| import dmd.dmodule; | |
| import dmd.doc; | |
| import dmd.dsymbol; | |
| import dmd.dsymbolsem; | |
| import dmd.dtemplate; | |
| import dmd.expression; | |
| import dmd.errors; | |
| import dmd.func; | |
| import dmd.globals; | |
| import dmd.id; | |
| import dmd.identifier; | |
| import dmd.common.outbuffer; | |
| import dmd.root.rmem; | |
| import dmd.root.speller; | |
| import dmd.statement; | |
| import dmd.target; | |
| import dmd.tokens; | |
| //version=LOGSEARCH; | |
| // List of flags that can be applied to this `Scope` | |
| enum SCOPE | |
| { | |
| ctor = 0x0001, /// constructor type | |
| noaccesscheck = 0x0002, /// don't do access checks | |
| condition = 0x0004, /// inside static if/assert condition | |
| debug_ = 0x0008, /// inside debug conditional | |
| constraint = 0x0010, /// inside template constraint | |
| invariant_ = 0x0020, /// inside invariant code | |
| require = 0x0040, /// inside in contract code | |
| ensure = 0x0060, /// inside out contract code | |
| contract = 0x0060, /// [mask] we're inside contract code | |
| ctfe = 0x0080, /// inside a ctfe-only expression | |
| compile = 0x0100, /// inside __traits(compile) | |
| ignoresymbolvisibility = 0x0200, /// ignore symbol visibility | |
| /// https://issues.dlang.org/show_bug.cgi?id=15907 | |
| Cfile = 0x0800, /// C semantics apply | |
| free = 0x8000, /// is on free list | |
| fullinst = 0x10000, /// fully instantiate templates | |
| alias_ = 0x20000, /// inside alias declaration. | |
| // The following are mutually exclusive | |
| printf = 0x4_0000, /// printf-style function | |
| scanf = 0x8_0000, /// scanf-style function | |
| } | |
| /// Flags that are carried along with a scope push() | |
| private enum PersistentFlags = | |
| SCOPE.contract | SCOPE.debug_ | SCOPE.ctfe | SCOPE.compile | SCOPE.constraint | | |
| SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility | | |
| SCOPE.printf | SCOPE.scanf | SCOPE.Cfile; | |
| struct Scope | |
| { | |
| Scope* enclosing; /// enclosing Scope | |
| Module _module; /// Root module | |
| ScopeDsymbol scopesym; /// current symbol | |
| FuncDeclaration func; /// function we are in | |
| Dsymbol parent; /// parent to use | |
| LabelStatement slabel; /// enclosing labelled statement | |
| SwitchStatement sw; /// enclosing switch statement | |
| Statement tryBody; /// enclosing _body of TryCatchStatement or TryFinallyStatement | |
| TryFinallyStatement tf; /// enclosing try finally statement | |
| ScopeGuardStatement os; /// enclosing scope(xxx) statement | |
| Statement sbreak; /// enclosing statement that supports "break" | |
| Statement scontinue; /// enclosing statement that supports "continue" | |
| ForeachStatement fes; /// if nested function for ForeachStatement, this is it | |
| Scope* callsc; /// used for __FUNCTION__, __PRETTY_FUNCTION__ and __MODULE__ | |
| Dsymbol inunion; /// != null if processing members of a union | |
| bool nofree; /// true if shouldn't free it | |
| bool inLoop; /// true if inside a loop (where constructor calls aren't allowed) | |
| int intypeof; /// in typeof(exp) | |
| VarDeclaration lastVar; /// Previous symbol used to prevent goto-skips-init | |
| /* If minst && !tinst, it's in definitely non-speculative scope (eg. module member scope). | |
| * If !minst && !tinst, it's in definitely speculative scope (eg. template constraint). | |
| * If minst && tinst, it's in instantiated code scope without speculation. | |
| * If !minst && tinst, it's in instantiated code scope with speculation. | |
| */ | |
| Module minst; /// root module where the instantiated templates should belong to | |
| TemplateInstance tinst; /// enclosing template instance | |
| CtorFlow ctorflow; /// flow analysis for constructors | |
| /// alignment for struct members | |
| AlignDeclaration aligndecl; | |
| /// C++ namespace this symbol is in | |
| CPPNamespaceDeclaration namespace; | |
| /// linkage for external functions | |
| LINK linkage = LINK.d; | |
| /// mangle type | |
| CPPMANGLE cppmangle = CPPMANGLE.def; | |
| /// inlining strategy for functions | |
| PragmaDeclaration inlining; | |
| /// visibility for class members | |
| Visibility visibility = Visibility(Visibility.Kind.public_); | |
| int explicitVisibility; /// set if in an explicit visibility attribute | |
| StorageClass stc; /// storage class | |
| DeprecatedDeclaration depdecl; /// customized deprecation message | |
| uint flags; | |
| // user defined attributes | |
| UserAttributeDeclaration userAttribDecl; | |
| DocComment* lastdc; /// documentation comment for last symbol at this scope | |
| uint[void*] anchorCounts; /// lookup duplicate anchor name count | |
| Identifier prevAnchor; /// qualified symbol name of last doc anchor | |
| AliasDeclaration aliasAsg; /// if set, then aliasAsg is being assigned a new value, | |
| /// do not set wasRead for it | |
| extern (D) __gshared Scope* freelist; | |
| extern (D) static Scope* alloc() | |
| { | |
| if (freelist) | |
| { | |
| Scope* s = freelist; | |
| freelist = s.enclosing; | |
| //printf("freelist %p\n", s); | |
| assert(s.flags & SCOPE.free); | |
| s.flags &= ~SCOPE.free; | |
| return s; | |
| } | |
| return new Scope(); | |
| } | |
| extern (D) static Scope* createGlobal(Module _module) | |
| { | |
| Scope* sc = Scope.alloc(); | |
| *sc = Scope.init; | |
| sc._module = _module; | |
| sc.minst = _module; | |
| sc.scopesym = new ScopeDsymbol(); | |
| sc.scopesym.symtab = new DsymbolTable(); | |
| // Add top level package as member of this global scope | |
| Dsymbol m = _module; | |
| while (m.parent) | |
| m = m.parent; | |
| m.addMember(null, sc.scopesym); | |
| m.parent = null; // got changed by addMember() | |
| if (_module.filetype == FileType.c) | |
| sc.flags |= SCOPE.Cfile; | |
| // Create the module scope underneath the global scope | |
| sc = sc.push(_module); | |
| sc.parent = _module; | |
| return sc; | |
| } | |
| extern (C++) Scope* copy() | |
| { | |
| Scope* sc = Scope.alloc(); | |
| *sc = this; | |
| /* https://issues.dlang.org/show_bug.cgi?id=11777 | |
| * The copied scope should not inherit fieldinit. | |
| */ | |
| sc.ctorflow.fieldinit = null; | |
| return sc; | |
| } | |
| extern (C++) Scope* push() | |
| { | |
| Scope* s = copy(); | |
| //printf("Scope::push(this = %p) new = %p\n", this, s); | |
| assert(!(flags & SCOPE.free)); | |
| s.scopesym = null; | |
| s.enclosing = &this; | |
| debug | |
| { | |
| if (enclosing) | |
| assert(!(enclosing.flags & SCOPE.free)); | |
| if (s == enclosing) | |
| { | |
| printf("this = %p, enclosing = %p, enclosing.enclosing = %p\n", s, &this, enclosing); | |
| } | |
| assert(s != enclosing); | |
| } | |
| s.slabel = null; | |
| s.nofree = false; | |
| s.ctorflow.fieldinit = ctorflow.fieldinit.arraydup; | |
| s.flags = (flags & PersistentFlags); | |
| s.lastdc = null; | |
| assert(&this != s); | |
| return s; | |
| } | |
| extern (C++) Scope* push(ScopeDsymbol ss) | |
| { | |
| //printf("Scope::push(%s)\n", ss.toChars()); | |
| Scope* s = push(); | |
| s.scopesym = ss; | |
| return s; | |
| } | |
| extern (C++) Scope* pop() | |
| { | |
| //printf("Scope::pop() %p nofree = %d\n", this, nofree); | |
| if (enclosing) | |
| enclosing.ctorflow.OR(ctorflow); | |
| ctorflow.freeFieldinit(); | |
| Scope* enc = enclosing; | |
| if (!nofree) | |
| { | |
| if (mem.isGCEnabled) | |
| this = this.init; | |
| enclosing = freelist; | |
| freelist = &this; | |
| flags |= SCOPE.free; | |
| } | |
| return enc; | |
| } | |
| /************************* | |
| * Similar to pop(), but the results in `this` are not folded | |
| * into `enclosing`. | |
| */ | |
| extern (D) void detach() | |
| { | |
| ctorflow.freeFieldinit(); | |
| enclosing = null; | |
| pop(); | |
| } | |
| extern (C++) Scope* startCTFE() | |
| { | |
| Scope* sc = this.push(); | |
| sc.flags = this.flags | SCOPE.ctfe; | |
| version (none) | |
| { | |
| /* TODO: Currently this is not possible, because we need to | |
| * unspeculative some types and symbols if they are necessary for the | |
| * final executable. Consider: | |
| * | |
| * struct S(T) { | |
| * string toString() const { return "instantiated"; } | |
| * } | |
| * enum x = S!int(); | |
| * void main() { | |
| * // To call x.toString in runtime, compiler should unspeculative S!int. | |
| * assert(x.toString() == "instantiated"); | |
| * } | |
| */ | |
| // If a template is instantiated from CT evaluated expression, | |
| // compiler can elide its code generation. | |
| sc.tinst = null; | |
| sc.minst = null; | |
| } | |
| return sc; | |
| } | |
| extern (C++) Scope* endCTFE() | |
| { | |
| assert(flags & SCOPE.ctfe); | |
| return pop(); | |
| } | |
| /******************************* | |
| * Merge results of `ctorflow` into `this`. | |
| * Params: | |
| * loc = for error messages | |
| * ctorflow = flow results to merge in | |
| */ | |
| extern (D) void merge(const ref Loc loc, const ref CtorFlow ctorflow) | |
| { | |
| if (!mergeCallSuper(this.ctorflow.callSuper, ctorflow.callSuper)) | |
| error(loc, "one path skips constructor"); | |
| const fies = ctorflow.fieldinit; | |
| if (this.ctorflow.fieldinit.length && fies.length) | |
| { | |
| FuncDeclaration f = func; | |
| if (fes) | |
| f = fes.func; | |
| auto ad = f.isMemberDecl(); | |
| assert(ad); | |
| foreach (i, v; ad.fields) | |
| { | |
| bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested()); | |
| auto fieldInit = &this.ctorflow.fieldinit[i]; | |
| const fiesCurrent = fies[i]; | |
| if (fieldInit.loc is Loc.init) | |
| fieldInit.loc = fiesCurrent.loc; | |
| if (!mergeFieldInit(this.ctorflow.fieldinit[i].csx, fiesCurrent.csx) && mustInit) | |
| { | |
| error(loc, "one path skips field `%s`", v.toChars()); | |
| } | |
| } | |
| } | |
| } | |
| /************************************ | |
| * Perform unqualified name lookup by following the chain of scopes up | |
| * until found. | |
| * | |
| * Params: | |
| * loc = location to use for error messages | |
| * ident = name to look up | |
| * pscopesym = if supplied and name is found, set to scope that ident was found in | |
| * flags = modify search based on flags | |
| * | |
| * Returns: | |
| * symbol if found, null if not | |
| */ | |
| extern (C++) Dsymbol search(const ref Loc loc, Identifier ident, Dsymbol* pscopesym, int flags = IgnoreNone) | |
| { | |
| version (LOGSEARCH) | |
| { | |
| printf("Scope.search(%p, '%s' flags=x%x)\n", &this, ident.toChars(), flags); | |
| // Print scope chain | |
| for (Scope* sc = &this; sc; sc = sc.enclosing) | |
| { | |
| if (!sc.scopesym) | |
| continue; | |
| printf("\tscope %s\n", sc.scopesym.toChars()); | |
| } | |
| static void printMsg(string txt, Dsymbol s) | |
| { | |
| printf("%.*s %s.%s, kind = '%s'\n", cast(int)txt.length, txt.ptr, | |
| s.parent ? s.parent.toChars() : "", s.toChars(), s.kind()); | |
| } | |
| } | |
| // This function is called only for unqualified lookup | |
| assert(!(flags & (SearchLocalsOnly | SearchImportsOnly))); | |
| /* If ident is "start at module scope", only look at module scope | |
| */ | |
| if (ident == Id.empty) | |
| { | |
| // Look for module scope | |
| for (Scope* sc = &this; sc; sc = sc.enclosing) | |
| { | |
| assert(sc != sc.enclosing); | |
| if (!sc.scopesym) | |
| continue; | |
| if (Dsymbol s = sc.scopesym.isModule()) | |
| { | |
| //printMsg("\tfound", s); | |
| if (pscopesym) | |
| *pscopesym = sc.scopesym; | |
| return s; | |
| } | |
| } | |
| return null; | |
| } | |
| Dsymbol checkAliasThis(AggregateDeclaration ad, Identifier ident, int flags, Expression* exp) | |
| { | |
| import dmd.mtype; | |
| if (!ad || !ad.aliasthis) | |
| return null; | |
| Declaration decl = ad.aliasthis.sym.isDeclaration(); | |
| if (!decl) | |
| return null; | |
| Type t = decl.type; | |
| ScopeDsymbol sds; | |
| TypeClass tc; | |
| TypeStruct ts; | |
| switch(t.ty) | |
| { | |
| case Tstruct: | |
| ts = cast(TypeStruct)t; | |
| sds = ts.sym; | |
| break; | |
| case Tclass: | |
| tc = cast(TypeClass)t; | |
| sds = tc.sym; | |
| break; | |
| case Tinstance: | |
| sds = (cast(TypeInstance)t).tempinst; | |
| break; | |
| case Tenum: | |
| sds = (cast(TypeEnum)t).sym; | |
| break; | |
| default: break; | |
| } | |
| if (!sds) | |
| return null; | |
| Dsymbol ret = sds.search(loc, ident, flags); | |
| if (ret) | |
| { | |
| *exp = new DotIdExp(loc, *exp, ad.aliasthis.ident); | |
| *exp = new DotIdExp(loc, *exp, ident); | |
| return ret; | |
| } | |
| if (!ts && !tc) | |
| return null; | |
| Dsymbol s; | |
| *exp = new DotIdExp(loc, *exp, ad.aliasthis.ident); | |
| if (ts && !(ts.att & AliasThisRec.tracing)) | |
| { | |
| ts.att = cast(AliasThisRec)(ts.att | AliasThisRec.tracing); | |
| s = checkAliasThis(sds.isAggregateDeclaration(), ident, flags, exp); | |
| ts.att = cast(AliasThisRec)(ts.att & ~AliasThisRec.tracing); | |
| } | |
| else if(tc && !(tc.att & AliasThisRec.tracing)) | |
| { | |
| tc.att = cast(AliasThisRec)(tc.att | AliasThisRec.tracing); | |
| s = checkAliasThis(sds.isAggregateDeclaration(), ident, flags, exp); | |
| tc.att = cast(AliasThisRec)(tc.att & ~AliasThisRec.tracing); | |
| } | |
| return s; | |
| } | |
| Dsymbol searchScopes(int flags) | |
| { | |
| for (Scope* sc = &this; sc; sc = sc.enclosing) | |
| { | |
| assert(sc != sc.enclosing); | |
| if (!sc.scopesym) | |
| continue; | |
| //printf("\tlooking in scopesym '%s', kind = '%s', flags = x%x\n", sc.scopesym.toChars(), sc.scopesym.kind(), flags); | |
| if (sc.scopesym.isModule()) | |
| flags |= SearchUnqualifiedModule; // tell Module.search() that SearchLocalsOnly is to be obeyed | |
| else if (sc.flags & SCOPE.Cfile && sc.scopesym.isStructDeclaration()) | |
| continue; // C doesn't have struct scope | |
| if (Dsymbol s = sc.scopesym.search(loc, ident, flags)) | |
| { | |
| if (flags & TagNameSpace) | |
| { | |
| // ImportC: if symbol is not a tag, look for it in tag table | |
| if (!s.isScopeDsymbol()) | |
| { | |
| auto ps = cast(void*)s in sc._module.tagSymTab; | |
| if (!ps) | |
| goto NotFound; | |
| s = *ps; | |
| } | |
| } | |
| if (!(flags & (SearchImportsOnly | IgnoreErrors)) && | |
| ident == Id.length && sc.scopesym.isArrayScopeSymbol() && | |
| sc.enclosing && sc.enclosing.search(loc, ident, null, flags)) | |
| { | |
| warning(s.loc, "array `length` hides other `length` name in outer scope"); | |
| } | |
| //printMsg("\tfound local", s); | |
| if (pscopesym) | |
| *pscopesym = sc.scopesym; | |
| return s; | |
| } | |
| NotFound: | |
| if (global.params.fixAliasThis) | |
| { | |
| Expression exp = new ThisExp(loc); | |
| Dsymbol aliasSym = checkAliasThis(sc.scopesym.isAggregateDeclaration(), ident, flags, &exp); | |
| if (aliasSym) | |
| { | |
| //printf("found aliassym: %s\n", aliasSym.toChars()); | |
| if (pscopesym) | |
| *pscopesym = new ExpressionDsymbol(exp); | |
| return aliasSym; | |
| } | |
| } | |
| // Stop when we hit a module, but keep going if that is not just under the global scope | |
| if (sc.scopesym.isModule() && !(sc.enclosing && !sc.enclosing.enclosing)) | |
| break; | |
| } | |
| return null; | |
| } | |
| if (this.flags & SCOPE.ignoresymbolvisibility) | |
| flags |= IgnoreSymbolVisibility; | |
| // First look in local scopes | |
| Dsymbol s = searchScopes(flags | SearchLocalsOnly); | |
| version (LOGSEARCH) if (s) printMsg("-Scope.search() found local", s); | |
| if (!s) | |
| { | |
| // Second look in imported modules | |
| s = searchScopes(flags | SearchImportsOnly); | |
| version (LOGSEARCH) if (s) printMsg("-Scope.search() found import", s); | |
| } | |
| return s; | |
| } | |
| extern (D) Dsymbol search_correct(Identifier ident) | |
| { | |
| if (global.gag) | |
| return null; // don't do it for speculative compiles; too time consuming | |
| /************************************************ | |
| * Given the failed search attempt, try to find | |
| * one with a close spelling. | |
| * Params: | |
| * seed = identifier to search for | |
| * cost = set to the cost, which rises with each outer scope | |
| * Returns: | |
| * Dsymbol if found, null if not | |
| */ | |
| extern (D) Dsymbol scope_search_fp(const(char)[] seed, out int cost) | |
| { | |
| //printf("scope_search_fp('%s')\n", seed); | |
| /* If not in the lexer's string table, it certainly isn't in the symbol table. | |
| * Doing this first is a lot faster. | |
| */ | |
| if (!seed.length) | |
| return null; | |
| Identifier id = Identifier.lookup(seed); | |
| if (!id) | |
| return null; | |
| Scope* sc = &this; | |
| Module.clearCache(); | |
| Dsymbol scopesym = null; | |
| Dsymbol s = sc.search(Loc.initial, id, &scopesym, IgnoreErrors); | |
| if (!s) | |
| return null; | |
| // Do not show `@disable`d declarations | |
| if (auto decl = s.isDeclaration()) | |
| if (decl.storage_class & STC.disable) | |
| return null; | |
| // Or `deprecated` ones if we're not in a deprecated scope | |
| if (s.isDeprecated() && !sc.isDeprecated()) | |
| return null; | |
| for (cost = 0; sc; sc = sc.enclosing, ++cost) | |
| if (sc.scopesym == scopesym) | |
| break; | |
| if (scopesym != s.parent) | |
| { | |
| ++cost; // got to the symbol through an import | |
| if (s.visible().kind == Visibility.Kind.private_) | |
| return null; | |
| } | |
| return s; | |
| } | |
| Dsymbol scopesym = null; | |
| // search for exact name first | |
| if (auto s = search(Loc.initial, ident, &scopesym, IgnoreErrors)) | |
| return s; | |
| return speller!scope_search_fp(ident.toString()); | |
| } | |
| /************************************ | |
| * Maybe `ident` was a C or C++ name. Check for that, | |
| * and suggest the D equivalent. | |
| * Params: | |
| * ident = unknown identifier | |
| * Returns: | |
| * D identifier string if found, null if not | |
| */ | |
| extern (D) static const(char)* search_correct_C(Identifier ident) | |
| { | |
| import dmd.astenums : Twchar; | |
| TOK tok; | |
| if (ident == Id.NULL) | |
| tok = TOK.null_; | |
| else if (ident == Id.TRUE) | |
| tok = TOK.true_; | |
| else if (ident == Id.FALSE) | |
| tok = TOK.false_; | |
| else if (ident == Id.unsigned) | |
| tok = TOK.uns32; | |
| else if (ident == Id.wchar_t) | |
| tok = target.c.wchar_tsize == 2 ? TOK.wchar_ : TOK.dchar_; | |
| else | |
| return null; | |
| return Token.toChars(tok); | |
| } | |
| /*************************** | |
| * Find the innermost scope with a symbol table. | |
| * Returns: | |
| * innermost scope, null if none | |
| */ | |
| extern (D) Scope* inner() return | |
| { | |
| for (Scope* sc = &this; sc; sc = sc.enclosing) | |
| { | |
| if (sc.scopesym) | |
| return sc; | |
| } | |
| return null; | |
| } | |
| /****************************** | |
| * Add symbol s to innermost symbol table. | |
| * Params: | |
| * s = symbol to insert | |
| * Returns: | |
| * null if already in table, `s` if not | |
| */ | |
| extern (D) Dsymbol insert(Dsymbol s) | |
| { | |
| //printf("insert() %s\n", s.toChars()); | |
| if (VarDeclaration vd = s.isVarDeclaration()) | |
| { | |
| if (lastVar) | |
| vd.lastVar = lastVar; | |
| lastVar = vd; | |
| } | |
| else if (WithScopeSymbol ss = s.isWithScopeSymbol()) | |
| { | |
| if (VarDeclaration vd = ss.withstate.wthis) | |
| { | |
| if (lastVar) | |
| vd.lastVar = lastVar; | |
| lastVar = vd; | |
| } | |
| return null; | |
| } | |
| auto scopesym = inner().scopesym; | |
| //printf("\t\tscopesym = %p\n", scopesym); | |
| if (!scopesym.symtab) | |
| scopesym.symtab = new DsymbolTable(); | |
| if (!(flags & SCOPE.Cfile)) | |
| return scopesym.symtabInsert(s); | |
| // ImportC insert | |
| if (!scopesym.symtabInsert(s)) // if already in table | |
| { | |
| Dsymbol s2 = scopesym.symtabLookup(s, s.ident); // s2 is existing entry | |
| return handleTagSymbols(this, s, s2, scopesym); | |
| } | |
| return s; // inserted | |
| } | |
| /******************************************** | |
| * Search enclosing scopes for ScopeDsymbol. | |
| */ | |
| ScopeDsymbol getScopesym() | |
| { | |
| for (Scope* sc = &this; sc; sc = sc.enclosing) | |
| { | |
| if (sc.scopesym) | |
| return sc.scopesym; | |
| } | |
| return null; // not found | |
| } | |
| /******************************************** | |
| * Search enclosing scopes for ClassDeclaration. | |
| */ | |
| extern (C++) ClassDeclaration getClassScope() | |
| { | |
| for (Scope* sc = &this; sc; sc = sc.enclosing) | |
| { | |
| if (!sc.scopesym) | |
| continue; | |
| if (ClassDeclaration cd = sc.scopesym.isClassDeclaration()) | |
| return cd; | |
| } | |
| return null; | |
| } | |
| /******************************************** | |
| * Search enclosing scopes for ClassDeclaration or StructDeclaration. | |
| */ | |
| extern (C++) AggregateDeclaration getStructClassScope() | |
| { | |
| for (Scope* sc = &this; sc; sc = sc.enclosing) | |
| { | |
| if (!sc.scopesym) | |
| continue; | |
| if (AggregateDeclaration ad = sc.scopesym.isClassDeclaration()) | |
| return ad; | |
| if (AggregateDeclaration ad = sc.scopesym.isStructDeclaration()) | |
| return ad; | |
| } | |
| return null; | |
| } | |
| /******************************************** | |
| * Find the lexically enclosing function (if any). | |
| * | |
| * This function skips through generated FuncDeclarations, | |
| * e.g. rewritten foreach bodies. | |
| * | |
| * Returns: the function or null | |
| */ | |
| inout(FuncDeclaration) getEnclosingFunction() inout | |
| { | |
| if (!this.func) | |
| return null; | |
| auto fd = cast(FuncDeclaration) this.func; | |
| // Look through foreach bodies rewritten as delegates | |
| while (fd.fes) | |
| { | |
| assert(fd.fes.func); | |
| fd = fd.fes.func; | |
| } | |
| return cast(inout(FuncDeclaration)) fd; | |
| } | |
| /******************************************* | |
| * For TemplateDeclarations, we need to remember the Scope | |
| * where it was declared. So mark the Scope as not | |
| * to be free'd. | |
| */ | |
| extern (D) void setNoFree() | |
| { | |
| //int i = 0; | |
| //printf("Scope::setNoFree(this = %p)\n", this); | |
| for (Scope* sc = &this; sc; sc = sc.enclosing) | |
| { | |
| //printf("\tsc = %p\n", sc); | |
| sc.nofree = true; | |
| assert(!(flags & SCOPE.free)); | |
| //assert(sc != sc.enclosing); | |
| //assert(!sc.enclosing || sc != sc.enclosing.enclosing); | |
| //if (++i == 10) | |
| // assert(0); | |
| } | |
| } | |
| /****************************** | |
| */ | |
| structalign_t alignment() | |
| { | |
| if (aligndecl) | |
| { | |
| auto ad = aligndecl.getAlignment(&this); | |
| return ad.salign; | |
| } | |
| else | |
| { | |
| structalign_t sa; | |
| sa.setDefault(); | |
| return sa; | |
| } | |
| } | |
| /********************************** | |
| * Checks whether the current scope (or any of its parents) is deprecated. | |
| * | |
| * Returns: `true` if this or any parent scope is deprecated, `false` otherwise` | |
| */ | |
| extern(C++) bool isDeprecated() @safe @nogc pure nothrow const | |
| { | |
| for (const(Dsymbol)* sp = &(this.parent); *sp; sp = &(sp.parent)) | |
| { | |
| if (sp.isDeprecated()) | |
| return true; | |
| } | |
| for (const(Scope)* sc2 = &this; sc2; sc2 = sc2.enclosing) | |
| { | |
| if (sc2.scopesym && sc2.scopesym.isDeprecated()) | |
| return true; | |
| // If inside a StorageClassDeclaration that is deprecated | |
| if (sc2.stc & STC.deprecated_) | |
| return true; | |
| } | |
| if (_module.md && _module.md.isdeprecated) | |
| { | |
| return true; | |
| } | |
| return false; | |
| } | |
| } |