1 /**
2  * A scope as defined by curly braces `{}`.
3  *
4  * Not to be confused with the `scope` storage class.
5  *
6  * Copyright:   Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
7  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
8  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dscope.d, _dscope.d)
10  * Documentation:  https://dlang.org/phobos/dmd_dscope.html
11  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dscope.d
12  */
13 
14 module dmd.dscope;
15 
16 import core.stdc.stdio;
17 import core.stdc.string;
18 import dmd.aggregate;
19 import dmd.arraytypes;
20 import dmd.attrib;
21 import dmd.ctorflow;
22 import dmd.dclass;
23 import dmd.declaration;
24 import dmd.dmodule;
25 import dmd.doc;
26 import dmd.dsymbol;
27 import dmd.dsymbolsem;
28 import dmd.dtemplate;
29 import dmd.expression;
30 import dmd.errors;
31 import dmd.func;
32 import dmd.globals;
33 import dmd.id;
34 import dmd.identifier;
35 import dmd.root.outbuffer;
36 import dmd.root.rmem;
37 import dmd.root.speller;
38 import dmd.statement;
39 import dmd.tokens;
40 
41 //version=LOGSEARCH;
42 
43 
44 // List of flags that can be applied to this `Scope`
45 enum SCOPE
46 {
47     ctor          = 0x0001,   /// constructor type
48     noaccesscheck = 0x0002,   /// don't do access checks
49     condition     = 0x0004,   /// inside static if/assert condition
50     debug_        = 0x0008,   /// inside debug conditional
51     constraint    = 0x0010,   /// inside template constraint
52     invariant_    = 0x0020,   /// inside invariant code
53     require       = 0x0040,   /// inside in contract code
54     ensure        = 0x0060,   /// inside out contract code
55     contract      = 0x0060,   /// [mask] we're inside contract code
56     ctfe          = 0x0080,   /// inside a ctfe-only expression
57     compile       = 0x0100,   /// inside __traits(compile)
58     ignoresymbolvisibility    = 0x0200,   /// ignore symbol visibility
59                                           /// https://issues.dlang.org/show_bug.cgi?id=15907
60     onlysafeaccess = 0x0400,  /// unsafe access is not allowed for @safe code
61     free          = 0x8000,   /// is on free list
62 
63     fullinst      = 0x10000,  /// fully instantiate templates
64     alias_        = 0x20000,  /// inside alias declaration.
65 
66     // The following are mutually exclusive
67     printf        = 0x4_0000, /// printf-style function
68     scanf         = 0x8_0000, /// scanf-style function
69 }
70 
71 /// Flags that are carried along with a scope push()
72 private enum PersistentFlags =
73     SCOPE.contract | SCOPE.debug_ | SCOPE.ctfe | SCOPE.compile | SCOPE.constraint |
74     SCOPE.noaccesscheck | SCOPE.onlysafeaccess | SCOPE.ignoresymbolvisibility |
75     SCOPE.printf | SCOPE.scanf;
76 
77 struct Scope
78 {
79     Scope* enclosing;               /// enclosing Scope
80 
81     Module _module;                 /// Root module
82     ScopeDsymbol scopesym;          /// current symbol
83     FuncDeclaration func;           /// function we are in
84     Dsymbol parent;                 /// parent to use
85     LabelStatement slabel;          /// enclosing labelled statement
86     SwitchStatement sw;             /// enclosing switch statement
87     Statement tryBody;              /// enclosing _body of TryCatchStatement or TryFinallyStatement
88     TryFinallyStatement tf;         /// enclosing try finally statement
89     ScopeGuardStatement os;            /// enclosing scope(xxx) statement
90     Statement sbreak;               /// enclosing statement that supports "break"
91     Statement scontinue;            /// enclosing statement that supports "continue"
92     ForeachStatement fes;           /// if nested function for ForeachStatement, this is it
93     Scope* callsc;                  /// used for __FUNCTION__, __PRETTY_FUNCTION__ and __MODULE__
94     Dsymbol inunion;                /// != null if processing members of a union
95     bool nofree;                    /// true if shouldn't free it
96     bool inLoop;                    /// true if inside a loop (where constructor calls aren't allowed)
97     int intypeof;                   /// in typeof(exp)
98     VarDeclaration lastVar;         /// Previous symbol used to prevent goto-skips-init
99 
100     /* If  minst && !tinst, it's in definitely non-speculative scope (eg. module member scope).
101      * If !minst && !tinst, it's in definitely speculative scope (eg. template constraint).
102      * If  minst &&  tinst, it's in instantiated code scope without speculation.
103      * If !minst &&  tinst, it's in instantiated code scope with speculation.
104      */
105     Module minst;                   /// root module where the instantiated templates should belong to
106     TemplateInstance tinst;         /// enclosing template instance
107 
108     CtorFlow ctorflow;              /// flow analysis for constructors
109 
110     /// alignment for struct members
111     AlignDeclaration aligndecl;
112 
113     /// C++ namespace this symbol is in
114     CPPNamespaceDeclaration namespace;
115 
116     /// linkage for external functions
117     LINK linkage = LINK.d;
118 
119     /// mangle type
120     CPPMANGLE cppmangle = CPPMANGLE.def;
121 
122     /// inlining strategy for functions
123     PragmaDeclaration inlining;
124 
125     /// visibility for class members
126     Visibility visibility = Visibility(Visibility.Kind.public_);
127     int explicitVisibility;         /// set if in an explicit visibility attribute
128 
129     StorageClass stc;               /// storage class
130 
131     DeprecatedDeclaration depdecl;  /// customized deprecation message
132 
133     uint flags;
134 
135     // user defined attributes
136     UserAttributeDeclaration userAttribDecl;
137 
138     DocComment* lastdc;        /// documentation comment for last symbol at this scope
139     uint[void*] anchorCounts;  /// lookup duplicate anchor name count
140     Identifier prevAnchor;     /// qualified symbol name of last doc anchor
141 
142     AliasDeclaration aliasAsg; /// if set, then aliasAsg is being assigned a new value,
143                                /// do not set wasRead for it
144 
145     extern (D) __gshared Scope* freelist;
146 
147     extern (D) static Scope* alloc()
148     {
149         if (freelist)
150         {
151             Scope* s = freelist;
152             freelist = s.enclosing;
153             //printf("freelist %p\n", s);
154             assert(s.flags & SCOPE.free);
155             s.flags &= ~SCOPE.free;
156             return s;
157         }
158         return new Scope();
159     }
160 
161     extern (D) static Scope* createGlobal(Module _module)
162     {
163         Scope* sc = Scope.alloc();
164         *sc = Scope.init;
165         sc._module = _module;
166         sc.minst = _module;
167         sc.scopesym = new ScopeDsymbol();
168         sc.scopesym.symtab = new DsymbolTable();
169         // Add top level package as member of this global scope
170         Dsymbol m = _module;
171         while (m.parent)
172             m = m.parent;
173         m.addMember(null, sc.scopesym);
174         m.parent = null; // got changed by addMember()
175         // Create the module scope underneath the global scope
176         sc = sc.push(_module);
177         sc.parent = _module;
178         return sc;
179     }
180 
181     extern (C++) Scope* copy()
182     {
183         Scope* sc = Scope.alloc();
184         *sc = this;
185         /* https://issues.dlang.org/show_bug.cgi?id=11777
186          * The copied scope should not inherit fieldinit.
187          */
188         sc.ctorflow.fieldinit = null;
189         return sc;
190     }
191 
192     extern (C++) Scope* push()
193     {
194         Scope* s = copy();
195         //printf("Scope::push(this = %p) new = %p\n", this, s);
196         assert(!(flags & SCOPE.free));
197         s.scopesym = null;
198         s.enclosing = &this;
199         debug
200         {
201             if (enclosing)
202                 assert(!(enclosing.flags & SCOPE.free));
203             if (s == enclosing)
204             {
205                 printf("this = %p, enclosing = %p, enclosing.enclosing = %p\n", s, &this, enclosing);
206             }
207             assert(s != enclosing);
208         }
209         s.slabel = null;
210         s.nofree = false;
211         s.ctorflow.fieldinit = ctorflow.fieldinit.arraydup;
212         s.flags = (flags & PersistentFlags);
213         s.lastdc = null;
214         assert(&this != s);
215         return s;
216     }
217 
218     extern (C++) Scope* push(ScopeDsymbol ss)
219     {
220         //printf("Scope::push(%s)\n", ss.toChars());
221         Scope* s = push();
222         s.scopesym = ss;
223         return s;
224     }
225 
226     extern (C++) Scope* pop()
227     {
228         //printf("Scope::pop() %p nofree = %d\n", this, nofree);
229         if (enclosing)
230             enclosing.ctorflow.OR(ctorflow);
231         ctorflow.freeFieldinit();
232 
233         Scope* enc = enclosing;
234         if (!nofree)
235         {
236             if (mem.isGCEnabled)
237                 this = this.init;
238             enclosing = freelist;
239             freelist = &this;
240             flags |= SCOPE.free;
241         }
242         return enc;
243     }
244 
245     /*************************
246      * Similar to pop(), but the results in `this` are not folded
247      * into `enclosing`.
248      */
249     extern (D) void detach()
250     {
251         ctorflow.freeFieldinit();
252         enclosing = null;
253         pop();
254     }
255 
256     extern (C++) Scope* startCTFE()
257     {
258         Scope* sc = this.push();
259         sc.flags = this.flags | SCOPE.ctfe;
260         version (none)
261         {
262             /* TODO: Currently this is not possible, because we need to
263              * unspeculative some types and symbols if they are necessary for the
264              * final executable. Consider:
265              *
266              * struct S(T) {
267              *   string toString() const { return "instantiated"; }
268              * }
269              * enum x = S!int();
270              * void main() {
271              *   // To call x.toString in runtime, compiler should unspeculative S!int.
272              *   assert(x.toString() == "instantiated");
273              * }
274              */
275             // If a template is instantiated from CT evaluated expression,
276             // compiler can elide its code generation.
277             sc.tinst = null;
278             sc.minst = null;
279         }
280         return sc;
281     }
282 
283     extern (C++) Scope* endCTFE()
284     {
285         assert(flags & SCOPE.ctfe);
286         return pop();
287     }
288 
289 
290     /*******************************
291      * Merge results of `ctorflow` into `this`.
292      * Params:
293      *   loc = for error messages
294      *   ctorflow = flow results to merge in
295      */
296     extern (D) void merge(const ref Loc loc, const ref CtorFlow ctorflow)
297     {
298         if (!mergeCallSuper(this.ctorflow.callSuper, ctorflow.callSuper))
299             error(loc, "one path skips constructor");
300 
301         const fies = ctorflow.fieldinit;
302         if (this.ctorflow.fieldinit.length && fies.length)
303         {
304             FuncDeclaration f = func;
305             if (fes)
306                 f = fes.func;
307             auto ad = f.isMemberDecl();
308             assert(ad);
309             foreach (i, v; ad.fields)
310             {
311                 bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested());
312                 auto fieldInit = &this.ctorflow.fieldinit[i];
313                 const fiesCurrent = fies[i];
314                 if (fieldInit.loc is Loc.init)
315                     fieldInit.loc = fiesCurrent.loc;
316                 if (!mergeFieldInit(this.ctorflow.fieldinit[i].csx, fiesCurrent.csx) && mustInit)
317                 {
318                     error(loc, "one path skips field `%s`", v.toChars());
319                 }
320             }
321         }
322     }
323 
324     extern (C++) Module instantiatingModule()
325     {
326         // TODO: in speculative context, returning 'module' is correct?
327         return minst ? minst : _module;
328     }
329 
330     /************************************
331      * Perform unqualified name lookup by following the chain of scopes up
332      * until found.
333      *
334      * Params:
335      *  loc = location to use for error messages
336      *  ident = name to look up
337      *  pscopesym = if supplied and name is found, set to scope that ident was found in
338      *  flags = modify search based on flags
339      *
340      * Returns:
341      *  symbol if found, null if not
342      */
343     extern (C++) Dsymbol search(const ref Loc loc, Identifier ident, Dsymbol* pscopesym, int flags = IgnoreNone)
344     {
345         version (LOGSEARCH)
346         {
347             printf("Scope.search(%p, '%s' flags=x%x)\n", &this, ident.toChars(), flags);
348             // Print scope chain
349             for (Scope* sc = &this; sc; sc = sc.enclosing)
350             {
351                 if (!sc.scopesym)
352                     continue;
353                 printf("\tscope %s\n", sc.scopesym.toChars());
354             }
355 
356             static void printMsg(string txt, Dsymbol s)
357             {
358                 printf("%.*s  %s.%s, kind = '%s'\n", cast(int)txt.length, txt.ptr,
359                     s.parent ? s.parent.toChars() : "", s.toChars(), s.kind());
360             }
361         }
362 
363         // This function is called only for unqualified lookup
364         assert(!(flags & (SearchLocalsOnly | SearchImportsOnly)));
365 
366         /* If ident is "start at module scope", only look at module scope
367          */
368         if (ident == Id.empty)
369         {
370             // Look for module scope
371             for (Scope* sc = &this; sc; sc = sc.enclosing)
372             {
373                 assert(sc != sc.enclosing);
374                 if (!sc.scopesym)
375                     continue;
376                 if (Dsymbol s = sc.scopesym.isModule())
377                 {
378                     //printMsg("\tfound", s);
379                     if (pscopesym)
380                         *pscopesym = sc.scopesym;
381                     return s;
382                 }
383             }
384             return null;
385         }
386 
387         Dsymbol checkAliasThis(AggregateDeclaration ad, Identifier ident, int flags, Expression* exp)
388         {
389             import dmd.mtype;
390             if (!ad || !ad.aliasthis)
391                 return null;
392 
393             Declaration decl = ad.aliasthis.sym.isDeclaration();
394             if (!decl)
395                 return null;
396 
397             Type t = decl.type;
398             ScopeDsymbol sds;
399             TypeClass tc;
400             TypeStruct ts;
401             switch(t.ty)
402             {
403                 case Tstruct:
404                     ts = cast(TypeStruct)t;
405                     sds = ts.sym;
406                     break;
407                 case Tclass:
408                     tc = cast(TypeClass)t;
409                     sds = tc.sym;
410                     break;
411                 case Tinstance:
412                     sds = (cast(TypeInstance)t).tempinst;
413                     break;
414                 case Tenum:
415                     sds = (cast(TypeEnum)t).sym;
416                     break;
417                 default: break;
418             }
419 
420             if (!sds)
421                 return null;
422 
423             Dsymbol ret = sds.search(loc, ident, flags);
424             if (ret)
425             {
426                 *exp = new DotIdExp(loc, *exp, ad.aliasthis.ident);
427                 *exp = new DotIdExp(loc, *exp, ident);
428                 return ret;
429             }
430 
431             if (!ts && !tc)
432                 return null;
433 
434             Dsymbol s;
435             *exp = new DotIdExp(loc, *exp, ad.aliasthis.ident);
436             if (ts && !(ts.att & AliasThisRec.tracing))
437             {
438                 ts.att = cast(AliasThisRec)(ts.att | AliasThisRec.tracing);
439                 s = checkAliasThis(sds.isAggregateDeclaration(), ident, flags, exp);
440                 ts.att = cast(AliasThisRec)(ts.att & ~AliasThisRec.tracing);
441             }
442             else if(tc && !(tc.att & AliasThisRec.tracing))
443             {
444                 tc.att = cast(AliasThisRec)(tc.att | AliasThisRec.tracing);
445                 s = checkAliasThis(sds.isAggregateDeclaration(), ident, flags, exp);
446                 tc.att = cast(AliasThisRec)(tc.att & ~AliasThisRec.tracing);
447             }
448             return s;
449         }
450 
451         Dsymbol searchScopes(int flags)
452         {
453             for (Scope* sc = &this; sc; sc = sc.enclosing)
454             {
455                 assert(sc != sc.enclosing);
456                 if (!sc.scopesym)
457                     continue;
458                 //printf("\tlooking in scopesym '%s', kind = '%s', flags = x%x\n", sc.scopesym.toChars(), sc.scopesym.kind(), flags);
459 
460                 if (sc.scopesym.isModule())
461                     flags |= SearchUnqualifiedModule;        // tell Module.search() that SearchLocalsOnly is to be obeyed
462 
463                 if (Dsymbol s = sc.scopesym.search(loc, ident, flags))
464                 {
465                     if (!(flags & (SearchImportsOnly | IgnoreErrors)) &&
466                         ident == Id.length && sc.scopesym.isArrayScopeSymbol() &&
467                         sc.enclosing && sc.enclosing.search(loc, ident, null, flags))
468                     {
469                         warning(s.loc, "array `length` hides other `length` name in outer scope");
470                     }
471                     //printMsg("\tfound local", s);
472                     if (pscopesym)
473                         *pscopesym = sc.scopesym;
474                     return s;
475                 }
476 
477                 if (global.params.fixAliasThis)
478                 {
479                     Expression exp = new ThisExp(loc);
480                     Dsymbol aliasSym = checkAliasThis(sc.scopesym.isAggregateDeclaration(), ident, flags, &exp);
481                     if (aliasSym)
482                     {
483                         //printf("found aliassym: %s\n", aliasSym.toChars());
484                         if (pscopesym)
485                             *pscopesym = new ExpressionDsymbol(exp);
486                         return aliasSym;
487                     }
488                 }
489 
490                 // Stop when we hit a module, but keep going if that is not just under the global scope
491                 if (sc.scopesym.isModule() && !(sc.enclosing && !sc.enclosing.enclosing))
492                     break;
493             }
494             return null;
495         }
496 
497         if (this.flags & SCOPE.ignoresymbolvisibility)
498             flags |= IgnoreSymbolVisibility;
499 
500         // First look in local scopes
501         Dsymbol s = searchScopes(flags | SearchLocalsOnly);
502         version (LOGSEARCH) if (s) printMsg("-Scope.search() found local", s);
503         if (!s)
504         {
505             // Second look in imported modules
506             s = searchScopes(flags | SearchImportsOnly);
507             version (LOGSEARCH) if (s) printMsg("-Scope.search() found import", s);
508         }
509         return s;
510     }
511 
512     extern (D) Dsymbol search_correct(Identifier ident)
513     {
514         if (global.gag)
515             return null; // don't do it for speculative compiles; too time consuming
516 
517         /************************************************
518          * Given the failed search attempt, try to find
519          * one with a close spelling.
520          * Params:
521          *      seed = identifier to search for
522          *      cost = set to the cost, which rises with each outer scope
523          * Returns:
524          *      Dsymbol if found, null if not
525          */
526         extern (D) Dsymbol scope_search_fp(const(char)[] seed, out int cost)
527         {
528             //printf("scope_search_fp('%s')\n", seed);
529             /* If not in the lexer's string table, it certainly isn't in the symbol table.
530              * Doing this first is a lot faster.
531              */
532             if (!seed.length)
533                 return null;
534             Identifier id = Identifier.lookup(seed);
535             if (!id)
536                 return null;
537             Scope* sc = &this;
538             Module.clearCache();
539             Dsymbol scopesym = null;
540             Dsymbol s = sc.search(Loc.initial, id, &scopesym, IgnoreErrors);
541             if (!s)
542                 return null;
543 
544             // Do not show `@disable`d declarations
545             if (auto decl = s.isDeclaration())
546                 if (decl.storage_class & STC.disable)
547                     return null;
548             // Or `deprecated` ones if we're not in a deprecated scope
549             if (s.isDeprecated() && !sc.isDeprecated())
550                 return null;
551 
552             for (cost = 0; sc; sc = sc.enclosing, ++cost)
553                 if (sc.scopesym == scopesym)
554                     break;
555             if (scopesym != s.parent)
556             {
557                 ++cost; // got to the symbol through an import
558                 if (s.visible().kind == Visibility.Kind.private_)
559                     return null;
560             }
561             return s;
562         }
563 
564         Dsymbol scopesym = null;
565         // search for exact name first
566         if (auto s = search(Loc.initial, ident, &scopesym, IgnoreErrors))
567             return s;
568         return speller!scope_search_fp(ident.toString());
569     }
570 
571     /************************************
572      * Maybe `ident` was a C or C++ name. Check for that,
573      * and suggest the D equivalent.
574      * Params:
575      *  ident = unknown identifier
576      * Returns:
577      *  D identifier string if found, null if not
578      */
579     extern (D) static const(char)* search_correct_C(Identifier ident)
580     {
581         TOK tok;
582         if (ident == Id.NULL)
583             tok = TOK.null_;
584         else if (ident == Id.TRUE)
585             tok = TOK.true_;
586         else if (ident == Id.FALSE)
587             tok = TOK.false_;
588         else if (ident == Id.unsigned)
589             tok = TOK.uns32;
590         else if (ident == Id.wchar_t)
591             tok = global.params.targetOS == TargetOS.Windows ? TOK.wchar_ : TOK.dchar_;
592         else
593             return null;
594         return Token.toChars(tok);
595     }
596 
597     extern (D) Dsymbol insert(Dsymbol s)
598     {
599         if (VarDeclaration vd = s.isVarDeclaration())
600         {
601             if (lastVar)
602                 vd.lastVar = lastVar;
603             lastVar = vd;
604         }
605         else if (WithScopeSymbol ss = s.isWithScopeSymbol())
606         {
607             if (VarDeclaration vd = ss.withstate.wthis)
608             {
609                 if (lastVar)
610                     vd.lastVar = lastVar;
611                 lastVar = vd;
612             }
613             return null;
614         }
615         for (Scope* sc = &this; sc; sc = sc.enclosing)
616         {
617             //printf("\tsc = %p\n", sc);
618             if (sc.scopesym)
619             {
620                 //printf("\t\tsc.scopesym = %p\n", sc.scopesym);
621                 if (!sc.scopesym.symtab)
622                     sc.scopesym.symtab = new DsymbolTable();
623                 return sc.scopesym.symtabInsert(s);
624             }
625         }
626         assert(0);
627     }
628 
629     /********************************************
630      * Search enclosing scopes for ClassDeclaration.
631      */
632     extern (C++) ClassDeclaration getClassScope()
633     {
634         for (Scope* sc = &this; sc; sc = sc.enclosing)
635         {
636             if (!sc.scopesym)
637                 continue;
638             ClassDeclaration cd = sc.scopesym.isClassDeclaration();
639             if (cd)
640                 return cd;
641         }
642         return null;
643     }
644 
645     /********************************************
646      * Search enclosing scopes for ClassDeclaration.
647      */
648     extern (C++) AggregateDeclaration getStructClassScope()
649     {
650         for (Scope* sc = &this; sc; sc = sc.enclosing)
651         {
652             if (!sc.scopesym)
653                 continue;
654             AggregateDeclaration ad = sc.scopesym.isClassDeclaration();
655             if (ad)
656                 return ad;
657             ad = sc.scopesym.isStructDeclaration();
658             if (ad)
659                 return ad;
660         }
661         return null;
662     }
663 
664     /*******************************************
665      * For TemplateDeclarations, we need to remember the Scope
666      * where it was declared. So mark the Scope as not
667      * to be free'd.
668      */
669     extern (D) void setNoFree()
670     {
671         //int i = 0;
672         //printf("Scope::setNoFree(this = %p)\n", this);
673         for (Scope* sc = &this; sc; sc = sc.enclosing)
674         {
675             //printf("\tsc = %p\n", sc);
676             sc.nofree = true;
677             assert(!(flags & SCOPE.free));
678             //assert(sc != sc.enclosing);
679             //assert(!sc.enclosing || sc != sc.enclosing.enclosing);
680             //if (++i == 10)
681             //    assert(0);
682         }
683     }
684 
685     structalign_t alignment()
686     {
687         if (aligndecl)
688             return aligndecl.getAlignment(&this);
689         else
690             return STRUCTALIGN_DEFAULT;
691     }
692 
693     /**********************************
694     * Checks whether the current scope (or any of its parents) is deprecated.
695     *
696     * Returns: `true` if this or any parent scope is deprecated, `false` otherwise`
697     */
698     extern(C++) bool isDeprecated() const
699     {
700         for (const(Dsymbol)* sp = &(this.parent); *sp; sp = &(sp.parent))
701         {
702             if (sp.isDeprecated())
703                 return true;
704         }
705         for (const(Scope)* sc2 = &this; sc2; sc2 = sc2.enclosing)
706         {
707             if (sc2.scopesym && sc2.scopesym.isDeprecated())
708                 return true;
709 
710             // If inside a StorageClassDeclaration that is deprecated
711             if (sc2.stc & STC.deprecated_)
712                 return true;
713         }
714         if (_module.md && _module.md.isdeprecated)
715         {
716             return true;
717         }
718         return false;
719     }
720 }