Skip to content

Commit

Permalink
Issue 18314 - std.traits.getSymbolsByUDA only considers the first sym…
Browse files Browse the repository at this point in the history
…bol of an overload set
  • Loading branch information
Biotronic committed Jan 28, 2018
1 parent afa7d58 commit 9173971
Showing 1 changed file with 65 additions and 25 deletions.
90 changes: 65 additions & 25 deletions std/traits.d
Expand Up @@ -7824,38 +7824,58 @@ private template isDesiredUDA(alias attribute)
*/
template getSymbolsByUDA(alias symbol, alias attribute)
{
import std.format : format;
import std.meta : AliasSeq, Filter;

// translate a list of strings into symbols. mixing in the entire alias
// avoids trying to access the symbol, which could cause a privacy violation
template toSymbols(names...)
{
static if (names.length == 0)
alias toSymbols = AliasSeq!();
else
mixin("alias toSymbols = AliasSeq!(symbol.%s, toSymbols!(names[1..$]));"
.format(names[0]));
}

// filtering inaccessible members
enum isAccessibleMember(string name) = __traits(compiles, __traits(getMember, symbol, name));
alias accessibleMembers = Filter!(isAccessibleMember, __traits(allMembers, symbol));

// filtering not compiled members such as alias of basic types
enum hasSpecificUDA(string name) = mixin("hasUDA!(symbol." ~ name ~ ", attribute)");
enum isCorrectMember(string name) = __traits(compiles, hasSpecificUDA!(name));

alias correctMembers = Filter!(isCorrectMember, accessibleMembers);
alias membersWithUDA = toSymbols!(Filter!(hasSpecificUDA, correctMembers));

alias membersWithUDA = getSymbolsByUDAImpl!(symbol, attribute, __traits(allMembers, symbol));

// if the symbol itself has the UDA, tack it on to the front of the list
static if (hasUDA!(symbol, attribute))
alias getSymbolsByUDA = AliasSeq!(symbol, membersWithUDA);
else
alias getSymbolsByUDA = membersWithUDA;
}

template getSymbolsByUDAImpl(alias symbol, alias attribute, names...)
{
import std.meta : Alias, AliasSeq, Filter;
static if (names.length == 0)
{
alias getSymbolsByUDAImpl = AliasSeq!();
}
else
{
alias tail = getSymbolsByUDAImpl!(symbol, attribute, names[1 .. $]);

// Filtering inaccessible members.
static if (!__traits(compiles, __traits(getMember, symbol, names[0])))
{
alias getSymbolsByUDAImpl = tail;
}
else
{
alias member = Alias!(__traits(getMember, symbol, names[0]));

// Filtering not compiled members such as alias of basic types.
static if (!__traits(compiles, hasUDA!(member, attribute)))
{
alias getSymbolsByUDAImpl = tail;
}
// Get overloads for functions, in case different overloads have different sets of UDAs.
else static if (isFunction!member)
{
enum hasSpecificUDA(alias member) = hasUDA!(member, attribute);
alias getSymbolsByUDAImpl = Filter!(hasSpecificUDA, __traits(getOverloads, symbol, names[0]));
}
else static if (hasUDA!(member, attribute))
{
alias getSymbolsByUDAImpl = AliasSeq!(member, tail);
}
else
{
alias getSymbolsByUDAImpl = tail;
}
}
}
}

///
@safe unittest
{
Expand Down Expand Up @@ -7921,6 +7941,26 @@ template getSymbolsByUDA(alias symbol, alias attribute)
static assert(getSymbolsByUDA!(D,UDA).length == 0);
}

// Issue 18314
@safe unittest
{
enum lol;
enum lal;

struct A
{
@lal
int n;
@lal
void foo();
@lol
void foo(int a);
}

assert(getSymbolsByUDA!(A, lal).length == 2);
assert(getSymbolsByUDA!(A, lol).length == 1);
}

// #15335: getSymbolsByUDA fails if type has private members
@safe unittest
{
Expand Down

0 comments on commit 9173971

Please sign in to comment.