Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 31 additions & 20 deletions compiler/src/dmd/attribsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -33,55 +33,66 @@ import dmd.expressionsem;
import dmd.location;
import dmd.root.array; // for each


/**
* Retrieves the attributes associated with a UserAttributeDeclaration.
* Returns:
* A pointer to Expressions containing the attributes, or null if none exist.
*/
Expressions* getAttributes(UserAttributeDeclaration a)
{
if (!a.userAttribDecl && (!a.atts || !a.atts.length))
return null;

if (auto sc = a._scope)
{
a._scope = null;
arrayExpressionSemantic(a.atts.peekSlice(), sc);
if (a.atts)
arrayExpressionSemantic(a.atts.peekSlice(), sc);
}

auto exps = new Expressions();

if (a.userAttribDecl && a.userAttribDecl !is a)
exps.push(new TupleExp(Loc.initial, a.userAttribDecl.getAttributes()));
{
if (auto parentAtts = a.userAttribDecl.getAttributes())
exps.push(new TupleExp(Loc.initial, parentAtts));
}

if (a.atts && a.atts.length)
exps.push(new TupleExp(Loc.initial, a.atts));

return exps;
}

/**
* Iterates the UDAs attached to the given symbol.
*
* Params:
* sym = the symbol to get the UDAs from
* sc = scope to use for semantic analysis of UDAs
* dg = called once for each UDA
* sym = the symbol to get the UDAs from
* sc = scope to use for semantic analysis of UDAs
* dg = called once for each UDA
*
* Returns:
* If `dg` returns `!= 0`, stops the iteration and returns that value.
* Otherwise, returns 0.
* If `dg` returns `!= 0`, stops the iteration and returns that value.
* Otherwise, returns 0.
*/
int foreachUda(Dsymbol sym, Scope* sc, int delegate(Expression) dg)
{
if (!sym.userAttribDecl)
return 0;

auto udas = sym.userAttribDecl.getAttributes();
if (!udas)
return 0;

arrayExpressionSemantic(udas.peekSlice(), sc, true);

return udas.each!((uda) {
if (!uda.isTupleExp())
return 0;

auto exps = uda.isTupleExp().exps;

return exps.each!((e) {
assert(e);

if (auto result = dg(e))
return result;
if (!uda) return 0;

return 0;
});
if (auto te = uda.isTupleExp())
return te.exps.each!((e) => dg(e));
else
return dg(uda);
});
}
51 changes: 51 additions & 0 deletions compiler/test/runnable/test22287.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
TEST_OUTPUT:
---
---
*/

/**
* Regression test for UDA handling refactoring.
* Covers single attributes, tuple attributes, and recursive collection.
* https://github.com/dlang/dmd/pull/22287
*/
module test22287;

struct MyUda {}
struct AnotherUda { int x; }

// 1. Single attribute (tests the new 'else' branch in foreachUda)
@MyUda void testSingle() {}

// 2. Multiple attributes in a tuple (tests TupleExp handling)
@(MyUda, AnotherUda(42)) void testMultiple() {}

// 3. Nested attributes (tests recursive collection in getAttributes)
@MyUda
{
@AnotherUda(100) int testNested;
}

void main()
{
// Check single attribute
alias attrs1 = __traits(getAttributes, testSingle);
static assert(attrs1.length == 1);
static assert(is(attrs1[0] == MyUda));

// Check multiple attributes
alias attrs2 = __traits(getAttributes, testMultiple);
static assert(attrs2.length == 2);
static assert(is(attrs2[0] == MyUda));
static assert(attrs2[1].x == 42);

// Check nested attributes collection
alias attrs3 = __traits(getAttributes, testNested);
static assert(attrs3.length == 2);
static assert(is(attrs3[0] == MyUda));
static assert(attrs3[1].x == 100);

// Verify main itself (standalone check)
alias attrsMain = __traits(getAttributes, main);
static assert(attrsMain.length == 0);
}
Loading