| /** |
| * Defines declarations of various attributes. |
| * |
| * The term 'attribute' refers to things that can apply to a larger scope than a single declaration. |
| * Among them are: |
| * - Alignment (`align(8)`) |
| * - User defined attributes (`@UDA`) |
| * - Function Attributes (`@safe`) |
| * - Storage classes (`static`, `__gshared`) |
| * - Mixin declarations (`mixin("int x;")`) |
| * - Conditional compilation (`static if`, `static foreach`) |
| * - Linkage (`extern(C)`) |
| * - Anonymous structs / unions |
| * - Protection (`private`, `public`) |
| * - Deprecated declarations (`@deprecated`) |
| * |
| * Copyright: Copyright (C) 1999-2025 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/compiler/src/dmd/attrib.d, _attrib.d) |
| * Documentation: https://dlang.org/phobos/dmd_attrib.html |
| * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/attrib.d |
| */ |
| |
| module dmd.attrib; |
| |
| import dmd.aggregate; |
| import dmd.arraytypes; |
| import dmd.astenums; |
| import dmd.cond; |
| import dmd.declaration; |
| import dmd.dmodule; |
| import dmd.dscope; |
| import dmd.dsymbol; |
| import dmd.expression; |
| import dmd.func; |
| import dmd.hdrgen : visibilityToBuffer; |
| import dmd.id; |
| import dmd.identifier; |
| import dmd.location; |
| import dmd.mtype; |
| import dmd.objc; // for objc.addSymbols |
| import dmd.common.outbuffer; |
| import dmd.root.array; // for each |
| import dmd.visitor; |
| |
| /*********************************************************** |
| * Abstract attribute applied to Dsymbol's used as a common |
| * ancestor for storage classes (StorageClassDeclaration), |
| * linkage (LinkageDeclaration) and others. |
| */ |
| extern (C++) abstract class AttribDeclaration : Dsymbol |
| { |
| Dsymbols* decl; /// Dsymbol's affected by this AttribDeclaration |
| |
| extern (D) this(Dsymbols* decl) @safe |
| { |
| super(DSYM.attribDeclaration); |
| this.decl = decl; |
| } |
| |
| extern (D) this(Loc loc, Dsymbols* decl) @safe |
| { |
| super(DSYM.attribDeclaration, loc, null); |
| this.decl = decl; |
| } |
| |
| extern (D) this(Loc loc, Identifier ident, Dsymbols* decl) @safe |
| { |
| super(DSYM.attribDeclaration, loc, ident); |
| this.decl = decl; |
| } |
| |
| /**************************************** |
| * Create a new scope if one or more given attributes |
| * are different from the sc's. |
| * If the returned scope != sc, the caller should pop |
| * the scope after it used. |
| */ |
| extern (D) static Scope* createNewScope(Scope* sc, STC stc, LINK linkage, |
| CPPMANGLE cppmangle, Visibility visibility, bool explicitVisibility, |
| AlignDeclaration aligndecl, PragmaDeclaration inlining) |
| { |
| Scope* sc2 = sc; |
| if (stc != sc.stc || |
| linkage != sc.linkage || |
| cppmangle != sc.cppmangle || |
| explicitVisibility != sc.explicitVisibility || |
| visibility != sc.visibility || |
| aligndecl !is sc.aligndecl || |
| inlining != sc.inlining) |
| { |
| // create new one for changes |
| sc2 = sc.copy(); |
| sc2.stc = stc; |
| sc2.linkage = linkage; |
| sc2.cppmangle = cppmangle; |
| sc2.visibility = visibility; |
| sc2.explicitVisibility = explicitVisibility; |
| sc2.aligndecl = aligndecl; |
| sc2.inlining = inlining; |
| } |
| return sc2; |
| } |
| |
| override const(char)* kind() const |
| { |
| return "attribute"; |
| } |
| |
| /**************************************** |
| */ |
| override final void addObjcSymbols(ClassDeclarations* classes, ClassDeclarations* categories) |
| { |
| objc.addSymbols(this, classes, categories); |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * Storage classes applied to Dsymbols, e.g. `const int i;` |
| * |
| * <stc> <decl...> |
| */ |
| extern (C++) class StorageClassDeclaration : AttribDeclaration |
| { |
| STC stc; |
| |
| extern (D) this(STC stc, Dsymbols* decl) @safe |
| { |
| super(decl); |
| this.stc = stc; |
| this.dsym = DSYM.storageClassDeclaration; |
| } |
| |
| extern (D) this(Loc loc, STC stc, Dsymbols* decl) @safe |
| { |
| super(loc, decl); |
| this.stc = stc; |
| this.dsym = DSYM.storageClassDeclaration; |
| } |
| |
| override StorageClassDeclaration syntaxCopy(Dsymbol s) |
| { |
| assert(!s); |
| return new StorageClassDeclaration(stc, Dsymbol.arraySyntaxCopy(decl)); |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * Deprecation with an additional message applied to Dsymbols, |
| * e.g. `deprecated("Superseeded by foo") int bar;`. |
| * (Note that `deprecated int bar;` is currently represented as a |
| * StorageClassDeclaration with STC.deprecated_) |
| * |
| * `deprecated(<msg>) <decl...>` |
| */ |
| extern (C++) final class DeprecatedDeclaration : StorageClassDeclaration |
| { |
| Expression msg; /// deprecation message |
| const(char)* msgstr; /// cached string representation of msg |
| |
| extern (D) this(Expression msg, Dsymbols* decl) @safe |
| { |
| super(STC.deprecated_, decl); |
| this.msg = msg; |
| } |
| |
| override DeprecatedDeclaration syntaxCopy(Dsymbol s) |
| { |
| assert(!s); |
| return new DeprecatedDeclaration(msg.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl)); |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * Linkage attribute applied to Dsymbols, e.g. |
| * `extern(C) void foo()`. |
| * |
| * `extern(<linkage>) <decl...>` |
| */ |
| extern (C++) final class LinkDeclaration : AttribDeclaration |
| { |
| LINK linkage; /// either explicitly set or `default_` |
| |
| extern (D) this(Loc loc, LINK linkage, Dsymbols* decl) @safe |
| { |
| super(loc, null, decl); |
| //printf("LinkDeclaration(linkage = %d, decl = %p)\n", linkage, decl); |
| this.linkage = linkage; |
| this.dsym = DSYM.linkDeclaration; |
| } |
| |
| static LinkDeclaration create(Loc loc, LINK p, Dsymbols* decl) @safe |
| { |
| return new LinkDeclaration(loc, p, decl); |
| } |
| |
| override LinkDeclaration syntaxCopy(Dsymbol s) |
| { |
| assert(!s); |
| return new LinkDeclaration(loc, linkage, Dsymbol.arraySyntaxCopy(decl)); |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * Attribute declaring whether an external aggregate should be mangled as |
| * a struct or class in C++, e.g. `extern(C++, struct) class C { ... }`. |
| * This is required for correct name mangling on MSVC targets, |
| * see cppmanglewin.d for details. |
| * |
| * `extern(C++, <cppmangle>) <decl...>` |
| */ |
| extern (C++) final class CPPMangleDeclaration : AttribDeclaration |
| { |
| CPPMANGLE cppmangle; |
| |
| extern (D) this(Loc loc, CPPMANGLE cppmangle, Dsymbols* decl) @safe |
| { |
| super(loc, null, decl); |
| //printf("CPPMangleDeclaration(cppmangle = %d, decl = %p)\n", cppmangle, decl); |
| this.cppmangle = cppmangle; |
| this.dsym = DSYM.cppMangleDeclaration; |
| } |
| |
| override CPPMangleDeclaration syntaxCopy(Dsymbol s) |
| { |
| assert(!s); |
| return new CPPMangleDeclaration(loc, cppmangle, Dsymbol.arraySyntaxCopy(decl)); |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /** |
| * A node to represent an `extern(C++)` namespace attribute |
| * |
| * There are two ways to declarate a symbol as member of a namespace: |
| * `Nspace` and `CPPNamespaceDeclaration`. |
| * The former creates a scope for the symbol, and inject them in the |
| * parent scope at the same time. |
| * The later, this class, has no semantic implications and is only |
| * used for mangling. |
| * Additionally, this class allows one to use reserved identifiers |
| * (D keywords) in the namespace. |
| * |
| * A `CPPNamespaceDeclaration` can be created from an `Identifier` |
| * (already resolved) or from an `Expression`, which is CTFE-ed |
| * and can be either a `TupleExp`, in which can additional |
| * `CPPNamespaceDeclaration` nodes are created, or a `StringExp`. |
| * |
| * Note that this class, like `Nspace`, matches only one identifier |
| * part of a namespace. For the namespace `"foo::bar"`, |
| * the will be a `CPPNamespaceDeclaration` with its `ident` |
| * set to `"bar"`, and its `namespace` field pointing to another |
| * `CPPNamespaceDeclaration` with its `ident` set to `"foo"`. |
| */ |
| extern (C++) final class CPPNamespaceDeclaration : AttribDeclaration |
| { |
| /// CTFE-able expression, resolving to `TupleExp` or `StringExp` |
| Expression exp; |
| |
| extern (D) this(Loc loc, Identifier ident, Dsymbols* decl) @safe |
| { |
| super(loc, ident, decl); |
| this.dsym = DSYM.cppNamespaceDeclaration; |
| } |
| |
| extern (D) this(Loc loc, Expression exp, Dsymbols* decl) @safe |
| { |
| super(loc, null, decl); |
| this.dsym = DSYM.cppNamespaceDeclaration; |
| this.exp = exp; |
| } |
| |
| extern (D) this(Loc loc, Identifier ident, Expression exp, Dsymbols* decl, |
| CPPNamespaceDeclaration parent) @safe |
| { |
| super(loc, ident, decl); |
| this.dsym = DSYM.cppNamespaceDeclaration; |
| this.exp = exp; |
| this.cppnamespace = parent; |
| } |
| |
| override CPPNamespaceDeclaration syntaxCopy(Dsymbol s) |
| { |
| assert(!s); |
| return new CPPNamespaceDeclaration( |
| this.loc, this.ident, this.exp, Dsymbol.arraySyntaxCopy(this.decl), this.cppnamespace); |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * Visibility declaration for Dsymbols, e.g. `public int i;` |
| * |
| * `<visibility> <decl...>` or |
| * `package(<pkg_identifiers>) <decl...>` if `pkg_identifiers !is null` |
| */ |
| extern (C++) final class VisibilityDeclaration : AttribDeclaration |
| { |
| Visibility visibility; /// the visibility |
| Identifier[] pkg_identifiers; /// identifiers for `package(foo.bar)` or null |
| |
| /** |
| * Params: |
| * loc = source location of attribute token |
| * visibility = visibility attribute data |
| * decl = declarations which are affected by this visibility attribute |
| */ |
| extern (D) this(Loc loc, Visibility visibility, Dsymbols* decl) @safe |
| { |
| super(loc, null, decl); |
| this.dsym = DSYM.visibilityDeclaration; |
| this.visibility = visibility; |
| //printf("decl = %p\n", decl); |
| } |
| |
| /** |
| * Params: |
| * loc = source location of attribute token |
| * pkg_identifiers = list of identifiers for a qualified package name |
| * decl = declarations which are affected by this visibility attribute |
| */ |
| extern (D) this(Loc loc, Identifier[] pkg_identifiers, Dsymbols* decl) |
| { |
| super(loc, null, decl); |
| this.dsym = DSYM.visibilityDeclaration; |
| this.visibility.kind = Visibility.Kind.package_; |
| this.pkg_identifiers = pkg_identifiers; |
| if (pkg_identifiers.length > 0) |
| { |
| Dsymbol tmp; |
| Package.resolve(pkg_identifiers, &tmp, null); |
| visibility.pkg = tmp ? tmp.isPackage() : null; |
| } |
| } |
| |
| override VisibilityDeclaration syntaxCopy(Dsymbol s) |
| { |
| assert(!s); |
| |
| if (visibility.kind == Visibility.Kind.package_) |
| return new VisibilityDeclaration(this.loc, pkg_identifiers, Dsymbol.arraySyntaxCopy(decl)); |
| else |
| return new VisibilityDeclaration(this.loc, visibility, Dsymbol.arraySyntaxCopy(decl)); |
| } |
| |
| override const(char)* kind() const |
| { |
| return "visibility attribute"; |
| } |
| |
| override const(char)* toPrettyChars(bool) |
| { |
| assert(visibility.kind > Visibility.Kind.undefined); |
| OutBuffer buf; |
| visibilityToBuffer(buf, visibility); |
| return buf.extractChars(); |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * Alignment attribute for aggregates, members and variables. |
| * |
| * `align(<ealign>) <decl...>` or |
| * `align <decl...>` if `ealign` is null |
| */ |
| extern (C++) final class AlignDeclaration : AttribDeclaration |
| { |
| Expressions* exps; /// Expression(s) yielding the desired alignment, |
| /// the largest value wins |
| /// the actual alignment is Unknown until it's either set to the value of `ealign` |
| /// or the default if `ealign` is null ( / an error ocurred) |
| structalign_t salign; |
| |
| |
| extern (D) this(Loc loc, Expression exp, Dsymbols* decl) |
| { |
| super(loc, null, decl); |
| this.dsym = DSYM.alignDeclaration; |
| if (exp) |
| { |
| exps = new Expressions(exp); |
| } |
| } |
| |
| extern (D) this(Loc loc, Expressions* exps, Dsymbols* decl) @safe |
| { |
| super(loc, null, decl); |
| this.dsym = DSYM.alignDeclaration; |
| this.exps = exps; |
| } |
| |
| extern (D) this(Loc loc, structalign_t salign, Dsymbols* decl) @safe |
| { |
| super(loc, null, decl); |
| this.dsym = DSYM.alignDeclaration; |
| this.salign = salign; |
| } |
| |
| override AlignDeclaration syntaxCopy(Dsymbol s) |
| { |
| assert(!s); |
| return new AlignDeclaration(loc, |
| Expression.arraySyntaxCopy(exps), |
| Dsymbol.arraySyntaxCopy(decl)); |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * An anonymous struct/union (defined by `isunion`). |
| */ |
| extern (C++) final class AnonDeclaration : AttribDeclaration |
| { |
| bool isunion; /// whether it's a union |
| int sem; /// 1 if successful semantic() |
| uint anonoffset; /// offset of anonymous struct |
| uint anonstructsize; /// size of anonymous struct |
| uint anonalignsize; /// size of anonymous struct for alignment purposes |
| |
| extern (D) this(Loc loc, bool isunion, Dsymbols* decl) @safe |
| { |
| super(loc, null, decl); |
| this.dsym = DSYM.anonDeclaration; |
| this.isunion = isunion; |
| } |
| |
| override AnonDeclaration syntaxCopy(Dsymbol s) |
| { |
| assert(!s); |
| return new AnonDeclaration(loc, isunion, Dsymbol.arraySyntaxCopy(decl)); |
| } |
| |
| override const(char)* kind() const |
| { |
| return (isunion ? "anonymous union" : "anonymous struct"); |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * Pragma applied to Dsymbols, e.g. `pragma(inline, true) void foo`, |
| * but not PragmaStatement's like `pragma(msg, "hello");`. |
| * |
| * pragma(<ident>, <args>) |
| */ |
| extern (C++) final class PragmaDeclaration : AttribDeclaration |
| { |
| Expressions* args; /// parameters of this pragma |
| |
| extern (D) this(Loc loc, Identifier ident, Expressions* args, Dsymbols* decl) @safe |
| { |
| super(loc, ident, decl); |
| this.dsym = DSYM.pragmaDeclaration; |
| this.args = args; |
| } |
| |
| override PragmaDeclaration syntaxCopy(Dsymbol s) |
| { |
| //printf("PragmaDeclaration::syntaxCopy(%s)\n", toChars()); |
| assert(!s); |
| return new PragmaDeclaration(loc, ident, Expression.arraySyntaxCopy(args), Dsymbol.arraySyntaxCopy(decl)); |
| } |
| |
| override const(char)* kind() const |
| { |
| return "pragma"; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * A conditional compilation declaration, used for `version` |
| * / `debug` and specialized for `static if`. |
| * |
| * <condition> { <decl...> } else { <elsedecl> } |
| */ |
| extern (C++) class ConditionalDeclaration : AttribDeclaration |
| { |
| Condition condition; /// condition deciding whether decl or elsedecl applies |
| Dsymbols* elsedecl; /// array of Dsymbol's for else block |
| |
| extern (D) this(Loc loc, Condition condition, Dsymbols* decl, Dsymbols* elsedecl) @safe |
| { |
| super(loc, null, decl); |
| this.dsym = DSYM.conditionalDeclaration; |
| //printf("ConditionalDeclaration::ConditionalDeclaration()\n"); |
| this.condition = condition; |
| this.elsedecl = elsedecl; |
| } |
| |
| override ConditionalDeclaration syntaxCopy(Dsymbol s) |
| { |
| assert(!s); |
| return new ConditionalDeclaration(loc, condition.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl), Dsymbol.arraySyntaxCopy(elsedecl)); |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * `<scopesym> { |
| * static if (<condition>) { <decl> } else { <elsedecl> } |
| * }` |
| */ |
| extern (C++) final class StaticIfDeclaration : ConditionalDeclaration |
| { |
| ScopeDsymbol scopesym; /// enclosing symbol (e.g. module) where symbols will be inserted |
| bool addisdone = false; /// true if members have been added to scope |
| bool onStack = false; /// true if a call to `include` is currently active |
| |
| extern (D) this(Loc loc, Condition condition, Dsymbols* decl, Dsymbols* elsedecl) @safe |
| { |
| super(loc, condition, decl, elsedecl); |
| this.dsym = DSYM.staticIfDeclaration; |
| //printf("StaticIfDeclaration::StaticIfDeclaration()\n"); |
| } |
| |
| override StaticIfDeclaration syntaxCopy(Dsymbol s) |
| { |
| assert(!s); |
| return new StaticIfDeclaration(loc, condition.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl), Dsymbol.arraySyntaxCopy(elsedecl)); |
| } |
| |
| override const(char)* kind() const |
| { |
| return "static if"; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * Static foreach at declaration scope, like: |
| * static foreach (i; [0, 1, 2]){ } |
| */ |
| |
| extern (C++) final class StaticForeachDeclaration : AttribDeclaration |
| { |
| StaticForeach sfe; /// contains `static foreach` expansion logic |
| |
| ScopeDsymbol scopesym; /// cached enclosing scope (mimics `static if` declaration) |
| |
| /++ |
| `include` can be called multiple times, but a `static foreach` |
| should be expanded at most once. Achieved by caching the result |
| of the first call. We need both `cached` and `cache`, because |
| `null` is a valid value for `cache`. |
| +/ |
| bool onStack = false; |
| bool cached = false; |
| Dsymbols* cache = null; |
| |
| extern (D) this(StaticForeach sfe, Dsymbols* decl) @safe |
| { |
| super(sfe.loc, null, decl); |
| this.dsym = DSYM.staticForeachDeclaration; |
| this.sfe = sfe; |
| } |
| |
| override StaticForeachDeclaration syntaxCopy(Dsymbol s) |
| { |
| assert(!s); |
| return new StaticForeachDeclaration( |
| sfe.syntaxCopy(), |
| Dsymbol.arraySyntaxCopy(decl)); |
| } |
| |
| override const(char)* kind() const |
| { |
| return "static foreach"; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * Collection of declarations that stores foreach index variables in a |
| * local symbol table. Other symbols declared within are forwarded to |
| * another scope, like: |
| * |
| * static foreach (i; 0 .. 10) // loop variables for different indices do not conflict. |
| * { // this body is expanded into 10 ForwardingAttribDeclarations, where `i` has storage class STC.local |
| * mixin("enum x" ~ to!string(i) ~ " = i"); // ok, can access current loop variable |
| * } |
| * |
| * static foreach (i; 0.. 10) |
| * { |
| * pragma(msg, mixin("x" ~ to!string(i))); // ok, all 10 symbols are visible as they were forwarded to the global scope |
| * } |
| * |
| * static assert(!is(typeof(i))); // loop index variable is not visible outside of the static foreach loop |
| * |
| * A StaticForeachDeclaration generates one |
| * ForwardingAttribDeclaration for each expansion of its body. The |
| * AST of the ForwardingAttribDeclaration contains both the `static |
| * foreach` variables and the respective copy of the `static foreach` |
| * body. The functionality is achieved by using a |
| * ForwardingScopeDsymbol as the parent symbol for the generated |
| * declarations. |
| */ |
| |
| extern(C++) final class ForwardingAttribDeclaration : AttribDeclaration |
| { |
| ForwardingScopeDsymbol sym = null; |
| |
| this(Dsymbols* decl) @safe |
| { |
| super(decl); |
| this.dsym = DSYM.forwardingAttribDeclaration; |
| sym = new ForwardingScopeDsymbol(); |
| sym.symtab = new DsymbolTable(); |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * Mixin declarations, like: |
| * mixin("int x"); |
| * https://dlang.org/spec/module.html#mixin-declaration |
| */ |
| // Note: was CompileDeclaration |
| extern (C++) final class MixinDeclaration : AttribDeclaration |
| { |
| Expressions* exps; |
| ScopeDsymbol scopesym; |
| bool compiled; |
| |
| extern (D) this(Loc loc, Expressions* exps) @safe |
| { |
| super(loc, null, null); |
| //printf("MixinDeclaration(loc = %d)\n", loc.linnum); |
| this.dsym = DSYM.mixinDeclaration; |
| this.exps = exps; |
| } |
| |
| override MixinDeclaration syntaxCopy(Dsymbol s) |
| { |
| //printf("MixinDeclaration::syntaxCopy('%s')\n", toChars()); |
| return new MixinDeclaration(loc, Expression.arraySyntaxCopy(exps)); |
| } |
| |
| override const(char)* kind() const |
| { |
| return "mixin"; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * User defined attributes look like: |
| * @foo(args, ...) |
| * @(args, ...) |
| */ |
| extern (C++) final class UserAttributeDeclaration : AttribDeclaration |
| { |
| Expressions* atts; |
| |
| extern (D) this(Expressions* atts, Dsymbols* decl) @safe |
| { |
| super(decl); |
| this.dsym = DSYM.userAttributeDeclaration; |
| this.atts = atts; |
| } |
| |
| override UserAttributeDeclaration syntaxCopy(Dsymbol s) |
| { |
| //printf("UserAttributeDeclaration::syntaxCopy('%s')\n", toChars()); |
| assert(!s); |
| return new UserAttributeDeclaration(Expression.arraySyntaxCopy(this.atts), Dsymbol.arraySyntaxCopy(decl)); |
| } |
| |
| extern (D) static Expressions* concat(Expressions* udas1, Expressions* udas2) |
| { |
| Expressions* udas; |
| if (!udas1 || udas1.length == 0) |
| udas = udas2; |
| else if (!udas2 || udas2.length == 0) |
| udas = udas1; |
| else |
| { |
| /* Create a new tuple that combines them |
| * (do not append to left operand, as this is a copy-on-write operation) |
| */ |
| udas = new Expressions(2); |
| (*udas)[0] = new TupleExp(Loc.initial, udas1); |
| (*udas)[1] = new TupleExp(Loc.initial, udas2); |
| } |
| return udas; |
| } |
| |
| override const(char)* kind() const |
| { |
| return "UserAttribute"; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /** |
| * Returns `true` if the given symbol is a symbol declared in |
| * `core.attribute` and has the given identifier. |
| * |
| * This is used to determine if a symbol is a UDA declared in |
| * `core.attribute`. |
| * |
| * Params: |
| * sym = the symbol to check |
| * ident = the name of the expected UDA |
| */ |
| bool isCoreUda(Dsymbol sym, Identifier ident) |
| { |
| if (sym.ident != ident || !sym.parent) |
| return false; |
| |
| auto _module = sym.parent.isModule(); |
| return _module && _module.isCoreModule(Id.attribute); |
| } |
| |
| /** |
| * Iterates the UDAs attached to the given symbol, without performing semantic |
| * analysis. |
| * |
| * Use this function instead of `foreachUda` if semantic analysis of `sym` is |
| * still in progress. |
| * |
| * Params: |
| * sym = the symbol to get the UDAs from |
| * dg = called once for each UDA |
| * |
| * Returns: |
| * If `dg` returns `!= 0`, stops the iteration and returns that value. |
| * Otherwise, returns 0. |
| */ |
| int foreachUdaNoSemantic(Dsymbol sym, int delegate(Expression) dg) |
| { |
| if (sym.userAttribDecl is null || sym.userAttribDecl.atts is null) |
| return 0; |
| |
| foreach (exp; *sym.userAttribDecl.atts) |
| { |
| if (auto result = dg(exp)) |
| return result; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * Returns: true if the given expression is an enum from `core.attribute` named `id` |
| */ |
| bool isEnumAttribute(Expression e, Identifier id) |
| { |
| import dmd.attrib : isCoreUda; |
| import dmd.id : Id; |
| |
| // Logic based on dmd.objc.Supported.declaredAsOptionalCount |
| auto typeExp = e.isTypeExp; |
| if (!typeExp) |
| return false; |
| |
| auto typeEnum = typeExp.type.isTypeEnum(); |
| if (!typeEnum) |
| return false; |
| |
| if (isCoreUda(typeEnum.sym, id)) |
| return true; |
| |
| return false; |
| } |