Fix interface conformance checks.

- Now includes final interface methods
- Properly checks attributes
This commit is contained in:
Sönke Ludwig 2016-11-02 20:59:19 +01:00
parent 2d37e550bd
commit b96c20ef54

View file

@ -403,29 +403,39 @@ template checkInterfaceConformance(T, I) {
alias Members = AliasSeq!(__traits(allMembers, I)); alias Members = AliasSeq!(__traits(allMembers, I));
template checkMemberConformance(string mem) { template checkMemberConformance(string mem) {
alias Overloads = MemberFunctionsTuple!(I, mem); alias Overloads = AliasSeq!(__traits(getOverloads, I, mem));
template impl(size_t i) { template impl(size_t i) {
static if (i < Overloads.length) { static if (i < Overloads.length) {
alias F = Overloads[i]; alias F = Overloads[i];
alias FT = FunctionTypeOf!F; alias FT = FunctionTypeOf!F;
alias PT = ParameterTypeTuple!F; alias PT = ParameterTypeTuple!F;
alias RT = ReturnType!F; alias RT = ReturnType!F;
enum attribs = functionAttributeString!F(true);
static if (functionAttributes!F & FunctionAttribute.property) { static if (functionAttributes!F & FunctionAttribute.property) {
static if (PT.length > 0) { static if (PT.length > 0) {
static if (!is(typeof({ T t; return mixin("t."~mem) = PT.init; } ()) : RT)) static if (!is(typeof(mixin("function RT (ref T t)"~attribs~"{ return t."~mem~" = PT.init; }"))))
enum impl = T.stringof ~ " does not implement property setter \"" ~ mem ~ "\" of type " ~ FT.stringof; enum impl = T.stringof ~ " does not implement property setter \"" ~ mem ~ "\" of type " ~ FT.stringof;
else enum string impl = impl!(i+1); else enum string impl = impl!(i+1);
} else { } else {
static if (!is(typeof({ T t; return mixin("t."~mem); }()) : RT)) static if (!is(typeof(mixin("function RT(ref T t)"~attribs~"{ return t."~mem~"; }"))))
enum impl = T.stringof ~ " does not implement property getter \"" ~ mem ~ "\" of type " ~ FT.stringof; enum impl = T.stringof ~ " does not implement property getter \"" ~ mem ~ "\" of type " ~ FT.stringof;
else enum string impl = impl!(i+1); else enum string impl = impl!(i+1);
} }
} else { } else {
//pragma(msg, typeof({ T t; PT p; return mixin("t."~mem)(p); } ())); static if (is(RT == void)) {
static if (!is(typeof({ T t; PT p; return mixin("t."~mem)(p); } ()) : RT)) static if (!is(typeof(mixin("function void(ref T t, ref PT p)"~attribs~"{ t."~mem~"(p); }")))) {
static if (mem == "write" && PT.length == 2) {
auto f = mixin("function void(ref T t, ref PT p)"~attribs~"{ t."~mem~"(p); }");
}
enum impl = T.stringof ~ " does not implement method \"" ~ mem ~ "\" of type " ~ FT.stringof;
}
else enum string impl = impl!(i+1);
} else {
static if (!is(typeof(mixin("function RT(ref T t, ref PT p)"~attribs~"{ return t."~mem~"(p); }"))))
enum impl = T.stringof ~ " does not implement method \"" ~ mem ~ "\" of type " ~ FT.stringof; enum impl = T.stringof ~ " does not implement method \"" ~ mem ~ "\" of type " ~ FT.stringof;
else enum string impl = impl!(i+1); else enum string impl = impl!(i+1);
} }
}
} else enum string impl = null; } else enum string impl = null;
} }
alias checkMemberConformance = impl!0; alias checkMemberConformance = impl!0;
@ -433,7 +443,9 @@ template checkInterfaceConformance(T, I) {
template impl(size_t i) { template impl(size_t i) {
static if (i < Members.length) { static if (i < Members.length) {
static if (__traits(compiles, __traits(getMember, I, Members[i])))
enum mc = checkMemberConformance!(Members[i]); enum mc = checkMemberConformance!(Members[i]);
else enum mc = null;
static if (mc is null) enum impl = impl!(i+1); static if (mc is null) enum impl = impl!(i+1);
else enum impl = mc; else enum impl = mc;
} else enum string impl = null; } else enum string impl = null;
@ -470,6 +482,7 @@ unittest {
mixin validateInterfaceConformance!(OSClass, OutputStream); mixin validateInterfaceConformance!(OSClass, OutputStream);
static struct OSStruct { static struct OSStruct {
@safe:
void write(in ubyte[] bytes) {} void write(in ubyte[] bytes) {}
void flush() {} void flush() {}
void finalize() {} void finalize() {}
@ -479,6 +492,7 @@ unittest {
mixin validateInterfaceConformance!(OSStruct, OutputStream); mixin validateInterfaceConformance!(OSStruct, OutputStream);
static struct NonOSStruct { static struct NonOSStruct {
@safe:
void write(in ubyte[] bytes) {} void write(in ubyte[] bytes) {}
void flush(bool) {} void flush(bool) {}
void finalize() {} void finalize() {}
@ -487,4 +501,48 @@ unittest {
static assert(checkInterfaceConformance!(NonOSStruct, OutputStream) == static assert(checkInterfaceConformance!(NonOSStruct, OutputStream) ==
"NonOSStruct does not implement method \"flush\" of type @safe void()"); "NonOSStruct does not implement method \"flush\" of type @safe void()");
static struct NonOSStruct2 {
void write(in ubyte[] bytes) {} // not @safe
void flush(bool) {}
void finalize() {}
void write(InputStream stream, ulong nbytes) {}
}
static assert(checkInterfaceConformance!(NonOSStruct2, OutputStream) ==
"NonOSStruct2 does not implement method \"write\" of type @safe void(const(ubyte[]) bytes)");
}
string functionAttributeString(alias F)(bool restrictions_only)
{
import std.traits : FunctionAttribute, functionAttributes;
auto attribs = functionAttributes!F;
string ret;
with (FunctionAttribute) {
if (attribs & nogc) ret ~= " @nogc";
if (attribs & nothrow_) ret ~= " nothrow";
if (attribs & pure_) ret ~= " pure";
if (attribs & safe) ret ~= " @safe";
if (!restrictions_only) {
if (attribs & property) ret ~= " @property";
if (attribs & ref_) ret ~= " ref";
if (attribs & shared_) ret ~= " shared";
if (attribs & const_) ret ~= " const";
}
}
return ret;
}
string functionAttributeThisType(alias F)(string tname)
{
import std.traits : FunctionAttribute, functionAttributes;
auto attribs = functionAttributes!F;
string ret = tname;
with (FunctionAttribute) {
if (attribs & shared_) ret = "shared("~ret~")";
if (attribs & const_) ret = "const("~ret~")";
}
return ret;
} }