1 module introspection.callable; 2 3 import introspection.type; 4 import introspection.parameter; 5 import introspection.attribute; 6 import introspection.location; 7 import introspection.protection; 8 9 import std.traits; 10 import std.typecons; 11 12 version(unittest) { 13 import fluent.asserts; 14 } 15 16 /// Stores information about callalbles 17 struct Callable { 18 /// 19 string name; 20 21 /// 22 Type type; 23 24 /// 25 Type returns; 26 27 /// 28 Parameter[] parameters; 29 30 /// 31 Attribute[] attributes; 32 33 /// 34 Location location; 35 36 /// 37 Protection protection; 38 39 /// 40 bool isStatic; 41 } 42 43 /// Describes a callable 44 Callable describeCallable(alias T, size_t overloadIndex = 0)() if(isCallable!T) { 45 Parameter[] params; 46 params.length = arity!T; 47 48 size_t i; 49 static foreach (name; ParameterIdentifierTuple!T) { 50 params[i].name = name; 51 i++; 52 } 53 54 i = 0; 55 static foreach (P; Parameters!T) { 56 params[i].type = describeType!P; 57 i++; 58 } 59 60 i = 0; 61 static foreach (D; ParameterDefaults!T) { 62 static if(!is(D == void)) { 63 params[i].default_.value = D.stringof; 64 params[i].default_.exists = true; 65 } 66 i++; 67 } 68 69 i = 0; 70 static foreach (S; ParameterStorageClassTuple!T) { 71 static if(S == ParameterStorageClass.scope_) { 72 params[i].isScope = true; 73 } 74 75 static if(S == ParameterStorageClass.out_) { 76 params[i].isOut = true; 77 } 78 79 static if(S == ParameterStorageClass.ref_) { 80 params[i].isRef = true; 81 } 82 83 static if(S == ParameterStorageClass.lazy_) { 84 params[i].isLazy = true; 85 } 86 87 static if(S == ParameterStorageClass.return_) { 88 params[i].isReturn = true; 89 } 90 91 i++; 92 } 93 94 static if(__traits(compiles, __traits(getLocation, T))) { 95 enum location = __traits(getLocation, T); 96 } 97 else { 98 enum location = tuple("unknown", 0, 0); 99 } 100 101 enum attributes = describeAttributeList!(__traits(getAttributes, T)); 102 103 return Callable( 104 __traits(identifier, T), 105 describeType!(typeof(T)), 106 describeType!(ReturnType!T), 107 params, 108 attributes, 109 Location(location[0], location[1], location[2]), 110 __traits(getProtection, T).toProtection, 111 __traits(isStaticFunction, T) 112 ); 113 } 114 115 /// It should describe a function with no params that returns void 116 unittest { 117 void test() { } 118 119 auto result = describeCallable!test; 120 121 result.name.should.equal("test"); 122 result.type.name.should.equal("pure nothrow @nogc @safe void()"); 123 result.returns.name.should.equal("void"); 124 result.parameters.length.should.equal(0); 125 result.location.file.should.equal("source/introspection/callable.d"); 126 result.location.line.should.be.greaterThan(0); 127 result.location.column.should.equal(8); 128 } 129 130 /// It should describe a function with no params that returns ref int 131 unittest { 132 int val = 0; 133 ref int test() { return val; } 134 135 auto result = describeCallable!test; 136 137 result.name.should.equal("test"); 138 result.type.name.should.equal("pure nothrow @nogc ref @safe int()"); 139 result.returns.name.should.equal("int"); 140 result.parameters.length.should.equal(0); 141 } 142 143 /// It should describe a function with a parameter without a default value 144 unittest { 145 int val = 0; 146 ref int test(string a) { return val; } 147 148 auto result = describeCallable!test; 149 150 result.parameters.length.should.equal(1); 151 result.parameters[0].name.should.equal("a"); 152 result.parameters[0].type.name.should.equal("string"); 153 result.parameters[0].default_.value.should.equal(""); 154 result.parameters[0].default_.exists.should.equal(false); 155 } 156 157 /// It should describe a function with a parameter with a default value 158 unittest { 159 int val = 0; 160 ref int test(string a = "test") { return val; } 161 162 auto result = describeCallable!test; 163 164 result.parameters.length.should.equal(1); 165 result.parameters[0].name.should.equal("a"); 166 result.parameters[0].type.name.should.equal("string"); 167 result.parameters[0].default_.value.should.equal(`"test"`); 168 result.parameters[0].default_.exists.should.equal(true); 169 170 result.parameters[0].isLazy.should.equal(false); 171 result.parameters[0].isScope.should.equal(false); 172 result.parameters[0].isOut.should.equal(false); 173 result.parameters[0].isRef.should.equal(false); 174 result.parameters[0].isReturn.should.equal(false); 175 } 176 177 /// It should describe a function attributes 178 unittest { 179 int attr(int) { return 0; } 180 181 @("attribute1") @attr(1) 182 void test() { } 183 184 auto result = describeCallable!test; 185 186 result.attributes.length.should.equal(2); 187 result.attributes[0].name.should.equal(`"attribute1"`); 188 result.attributes[0].type.name.should.equal(`string`); 189 result.attributes[1].name.should.equal("0"); 190 result.attributes[1].type.name.should.equal(`int`); 191 } 192 193 /// It should find the parameter storage classes 194 unittest { 195 void test(scope Object, out int, ref int, lazy int, return Object) { } 196 197 auto result = describeCallable!test; 198 199 result.parameters[0].isScope.should.equal(true); 200 result.parameters[1].isOut.should.equal(true); 201 result.parameters[2].isRef.should.equal(true); 202 result.parameters[3].isLazy.should.equal(true); 203 result.parameters[4].isReturn.should.equal(true); 204 }