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 size_t overloadIndex; 44 45 /// 46 Variadic variadicStyle; 47 } 48 49 /// Describes a callable 50 Callable describeCallable(alias T, size_t overloadIndex = 0)() if(isCallable!T) { 51 Parameter[] params; 52 53 static if(__traits(compiles, arity!T)) { 54 params.length = arity!T; 55 } 56 57 size_t i; 58 static foreach (name; ParameterIdentifierTuple!T) { 59 if(i >= params.length) { 60 params.length = params.length + 1; 61 } 62 63 params[i].name = name; 64 i++; 65 } 66 67 i = 0; 68 static foreach (P; Parameters!T) { 69 params[i].type = describeType!P; 70 i++; 71 } 72 73 i = 0; 74 static foreach (D; ParameterDefaults!T) { 75 static if(!is(D == void)) { 76 params[i].default_.value = D.stringof; 77 params[i].default_.exists = true; 78 } 79 i++; 80 } 81 82 i = 0; 83 static foreach (S; ParameterStorageClassTuple!T) { 84 static if(S == ParameterStorageClass.scope_) { 85 params[i].isScope = true; 86 } 87 88 static if(S == ParameterStorageClass.out_) { 89 params[i].isOut = true; 90 } 91 92 static if(S == ParameterStorageClass.ref_) { 93 params[i].isRef = true; 94 } 95 96 static if(S == ParameterStorageClass.lazy_) { 97 params[i].isLazy = true; 98 } 99 100 static if(S == ParameterStorageClass.return_) { 101 params[i].isReturn = true; 102 } 103 104 i++; 105 } 106 107 static if(__traits(compiles, __traits(getLocation, T))) { 108 enum location = __traits(getLocation, T); 109 } 110 else { 111 enum location = tuple("unknown", 0, 0); 112 } 113 114 static if(__traits(compiles, describeAttributeList!(__traits(getAttributes, T)) ~ describeAttributeList!(__traits(getFunctionAttributes, T)))) { 115 enum attributes = describeAttributeList!(__traits(getAttributes, T)) ~ describeAttributeList!(__traits(getFunctionAttributes, T)); 116 } else { 117 enum attributes = []; 118 } 119 120 static if(__traits(compiles, __traits(identifier, T))) { 121 enum name = __traits(identifier, T); 122 } else { 123 enum name = ""; 124 } 125 126 static if(__traits(compiles, __traits(getProtection, T).toProtection)) { 127 auto protection = __traits(getProtection, T).toProtection; 128 } else { 129 Protection protection; 130 } 131 132 static if(__traits(compiles, describeType!(typeof(T)))) { 133 return Callable( 134 name, 135 describeType!(typeof(T)), 136 describeType!(ReturnType!T), 137 params, 138 attributes, 139 Location(location[0], location[1], location[2]), 140 protection, 141 __traits(isStaticFunction, T), 142 overloadIndex, 143 variadicFunctionStyle!T 144 ); 145 } else { 146 return Callable( 147 name, 148 Type(), 149 describeType!(ReturnType!T), 150 params, 151 attributes, 152 Location(location[0], location[1], location[2]), 153 protection, 154 __traits(isStaticFunction, T), 155 overloadIndex, 156 variadicFunctionStyle!T 157 ); 158 } 159 } 160 161 /// It should describe a function with no params that returns void 162 unittest { 163 void test() { } 164 165 auto result = describeCallable!test; 166 167 result.name.should.equal("test"); 168 result.type.name.should.equal("pure nothrow @nogc @safe void()"); 169 result.returns.name.should.equal("void"); 170 result.parameters.length.should.equal(0); 171 result.location.file.should.equal("source/introspection/callable.d"); 172 result.location.line.should.be.greaterThan(0); 173 result.location.column.should.equal(8); 174 result.variadicStyle.should.equal(Variadic.no); 175 } 176 177 /// It should describe a function with variadic args 178 unittest { 179 void test(char c, ...) { } 180 181 auto result = describeCallable!test; 182 183 result.parameters.length.should.equal(1); 184 result.parameters[0].name.should.equal("c"); 185 result.parameters[0].type.name.should.equal("char"); 186 187 result.variadicStyle.should.equal(Variadic.d); 188 } 189 190 /// It should describe a function with no params that returns ref int 191 unittest { 192 int val = 0; 193 ref int test() { return val; } 194 195 auto result = describeCallable!test; 196 197 result.name.should.equal("test"); 198 result.type.name.should.equal("pure nothrow @nogc ref @safe int()"); 199 result.returns.name.should.equal("int"); 200 result.parameters.length.should.equal(0); 201 } 202 203 /// It should describe a function with a parameter without a default value 204 unittest { 205 int val = 0; 206 ref int test(string a) { return val; } 207 208 auto result = describeCallable!test; 209 210 result.parameters.length.should.equal(1); 211 result.parameters[0].name.should.equal("a"); 212 result.parameters[0].type.name.should.equal("string"); 213 result.parameters[0].default_.value.should.equal(""); 214 result.parameters[0].default_.exists.should.equal(false); 215 } 216 217 /// It should describe a function with a parameter with a default value 218 unittest { 219 int val = 0; 220 ref int test(string a = "test") { return val; } 221 222 auto result = describeCallable!test; 223 224 result.parameters.length.should.equal(1); 225 result.parameters[0].name.should.equal("a"); 226 result.parameters[0].type.name.should.equal("string"); 227 result.parameters[0].default_.value.should.equal(`"test"`); 228 result.parameters[0].default_.exists.should.equal(true); 229 230 result.parameters[0].isLazy.should.equal(false); 231 result.parameters[0].isScope.should.equal(false); 232 result.parameters[0].isOut.should.equal(false); 233 result.parameters[0].isRef.should.equal(false); 234 result.parameters[0].isReturn.should.equal(false); 235 } 236 237 /// It should describe a function attributes 238 unittest { 239 int attr(int) { return 0; } 240 241 @("attribute1") @attr(1) 242 void test() { } 243 244 auto result = describeCallable!test; 245 246 result.attributes.length.should.equal(6); 247 result.attributes[0].name.should.equal(`"attribute1"`); 248 result.attributes[0].type.name.should.equal(`string`); 249 250 result.attributes[1].name.should.equal("0"); 251 result.attributes[1].type.name.should.equal(`int`); 252 253 result.attributes[2].name.should.equal(`"pure"`); 254 result.attributes[2].type.name.should.equal(`string`); 255 256 result.attributes[3].name.should.equal(`"nothrow"`); 257 result.attributes[3].type.name.should.equal(`string`); 258 259 result.attributes[4].name.should.equal(`"@nogc"`); 260 result.attributes[4].type.name.should.equal(`string`); 261 262 result.attributes[5].name.should.equal(`"@safe"`); 263 result.attributes[5].type.name.should.equal(`string`); 264 } 265 266 /// It should find the parameter storage classes 267 unittest { 268 void test(scope Object, out int, ref int, lazy int, return Object) { } 269 270 auto result = describeCallable!test; 271 272 result.parameters[0].isScope.should.equal(true); 273 result.parameters[1].isOut.should.equal(true); 274 result.parameters[2].isRef.should.equal(true); 275 result.parameters[3].isLazy.should.equal(true); 276 result.parameters[4].isReturn.should.equal(true); 277 } 278 279 /// It should describe an aliased function 280 unittest { 281 void test(scope Object, out int, ref int, lazy int, return Object) { } 282 283 auto testDescribe(T)(T func){ 284 return describeCallable!T; 285 } 286 287 auto result = testDescribe(&test); 288 289 result.name.should.equal(""); 290 result.parameters[0].isScope.should.equal(true); 291 result.parameters[1].isOut.should.equal(true); 292 result.parameters[2].isRef.should.equal(true); 293 result.parameters[3].isLazy.should.equal(true); 294 result.parameters[4].isReturn.should.equal(true); 295 }