1 /** 2 Date: 2015-2016, Joakim Brännström 3 License: MPL-2, Mozilla Public License 2.0 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 6 Extracted information of types. 7 8 TODO replace the fmt with a specialized formatting struct for the purpose 9 needed by TypeKind. fmt is namely a typeless strict that do not encode the 10 assumed number or arguments when it is used. In other words it do not contain 11 information regarding the number of '%s'. 12 */ 13 module cpptooling.data.kind; 14 15 import std.conv : to; 16 import std.string : format; 17 import std.traits; 18 import std.typecons : Nullable, Flag; 19 import logger = std.experimental.logger; 20 21 public import taggedalgebraic : Void; 22 23 import cpptooling.data.symbol.types : USRType; 24 25 version (unittest) { 26 import unit_threaded : Name, shouldEqual; 27 } else { 28 private struct Name { 29 string name_; 30 } 31 } 32 33 alias ArrayInfoIndex = Nullable!long; 34 35 struct FuncInfoParam { 36 USRType usr; 37 TypeAttr attr; 38 string id; 39 Flag!"isVariadic" isVariadic; 40 } 41 42 /// Convert an array of indexes to a string representation 43 string toRepr(const(ArrayInfoIndex[]) indexes) @safe pure { 44 import std.algorithm : map, joiner; 45 import std.conv : text; 46 47 // dfmt off 48 return indexes 49 // a null is a dynamic index 50 .map!(a => a.isNull ? "[]" : "[" ~ text(a.get) ~ "]") 51 .joiner 52 .text; 53 // dfmt on 54 } 55 56 bool isIncompleteArray(const(ArrayInfoIndex)[] indexes) @safe pure nothrow @nogc { 57 foreach (index; indexes) { 58 if (index.isNull) 59 return true; 60 } 61 62 return false; 63 } 64 65 /** Type representation and information. 66 */ 67 struct TypeKind { 68 import std.traits : isSomeString; 69 import taggedalgebraic : TaggedAlgebraic; 70 import cpptooling.data.kind_type_format; 71 72 this(T)(T info, USRType usr) @safe if (!is(T == TypeKind)) { 73 this.info = info; 74 this.usr = usr; 75 } 76 77 this(T)(T info) @safe if (!is(T == TypeKind)) { 78 this(info, USRType.init); 79 } 80 81 invariant { 82 final switch (this.info.kind) with (TypeKind.Info) { 83 case Kind.ctor: 84 // Assuming that a ctor or dtor must always have a id, otherwise 85 // unusable 86 goto case; 87 case Kind.dtor: 88 assert(info.id.length > 0); 89 break; 90 case Kind.array: 91 case Kind.func: 92 case Kind.funcSignature: 93 case Kind.primitive: 94 case Kind.record: 95 case Kind.simple: 96 case Kind.typeRef: 97 break; 98 case Kind.funcPtr: 99 assert(info.attrs.length > 0); 100 break; 101 case Kind.pointer: 102 assert(info.attrs.length > 0); 103 break; 104 case Kind.null_: 105 break; 106 } 107 } 108 109 /// Formatting information needed to reproduce the type and identifier. 110 static @safe pure nothrow @nogc union InternalInfo { 111 Void null_; 112 PrimitiveInfo primitive; 113 SimpleInfo simple; 114 ArrayInfo array; 115 116 FuncInfo func; 117 FuncPtrInfo funcPtr; 118 FuncSignatureInfo funcSignature; 119 120 RecordInfo record; 121 CtorInfo ctor; 122 DtorInfo dtor; 123 PointerInfo pointer; 124 TypeRefInfo typeRef; 125 } 126 127 alias Info = TaggedAlgebraic!InternalInfo; 128 129 Info info; 130 USRType usr; 131 132 pure @safe nothrow @nogc: 133 134 /** The type 'const int x[2][3]' 135 */ 136 static struct ArrayInfo { 137 /// int %s%s 138 ArrayFmt fmt; 139 /// [2, 3] 140 ArrayInfoIndex[] indexes; 141 /// usr for 'int' 142 USRType element; 143 } 144 145 /** The type 'extern int (*e_g)(int pa)'. 146 * 147 * attrs is only for the pointers, never the final pointee. 148 * In the example shown about it would have length 1. 149 * 150 * TODO improve formatting with more separation, f.e return, ptr and args. 151 * TODO add a USRType for the FuncPrototype. 152 */ 153 static struct FuncPtrInfo { 154 /// int (%s %s)(int pa) 155 FuncPtrFmt fmt; 156 /// USRs up the pointee 157 USRType pointee; 158 /// attributes of the pointer hierarchy. attr[0] is the right most ptr. 159 TypeAttr[] attrs; 160 } 161 162 /** The type of a function signature, 'void foo(int)'. 163 */ 164 static struct FuncSignatureInfo { 165 /// void %s(int) 166 FuncSignatureFmt fmt; 167 USRType return_; 168 TypeAttr returnAttr; 169 FuncInfoParam[] params; 170 } 171 172 /** The type of a function prototype, 'void foo(int)'. 173 * 174 * TODO consider changing the chain to be a FuncInfo referencing a FuncSignatureInfo. 175 * 176 * This coupled with FuncSignatureInfo having the USR of the signature 177 * would mean that it would be possible to merge/detect/find all those 178 * FuncInfo with the same symbol mangling/signature. 179 * 180 * Which is needed when doing cross-translation unit analyse to find 181 * connections between "points of interest. 182 * 183 * It would also lower the amount of data in a FuncInfo. 184 * 185 */ 186 static struct FuncInfo { 187 /// void %s(int) 188 FuncFmt fmt; 189 USRType return_; 190 TypeAttr returnAttr; 191 FuncInfoParam[] params; 192 } 193 194 /** The type of a ctor prototype, 'Class::Class(int)' 195 */ 196 static struct CtorInfo { 197 /// %s(int) 198 CtorFmt fmt; 199 /// Class 200 string id; 201 FuncInfoParam[] params; 202 } 203 204 /** The type of a dtor prototype, 'Class::~Class()' 205 */ 206 static struct DtorInfo { 207 /// ~%s() 208 DtorFmt fmt; 209 /// identifier, in the example it would be 'Class' 210 string id; 211 } 212 213 /** The type of a pointer (may be recursive), 'const int ** const x' 214 * 215 * attrs is only for the pointers, never the final pointee. 216 * In the example shown about it would have length 2. 217 */ 218 static struct PointerInfo { 219 /// int%s %s 220 PtrFmt fmt; 221 /// USRs up the pointee 222 USRType pointee; 223 /// attributes of the pointer hierarchy. attr[0] is the left most ptr. 224 TypeAttr[] attrs; 225 } 226 227 /** Representation of a typedef, 'typedef int tx' 228 * 229 * canonicalType is the final resolved in a chain of typedef's. 230 */ 231 static struct TypeRefInfo { 232 /// tx %s 233 SimpleFmt fmt; 234 /// usr of the child type 235 USRType typeRef; 236 /// usr of the canonical type 237 USRType canonicalRef; 238 } 239 240 /** Represent a primitive type. 241 * 242 * Similar to a $(D SimpleInfo) but used to distinguish primitive types 243 * from "other" simple representations. 244 * 245 * The type 'int x' would be: 246 */ 247 static struct PrimitiveInfo { 248 /// int %s 249 SimpleFmt fmt; 250 } 251 252 /** Textual representation of simple types. 253 * 254 * A simple type is one that do not need the features or distinction of the 255 * other infos. 256 * 257 * The type 'const int x' would be: 258 */ 259 static struct SimpleInfo { 260 /// int %s 261 SimpleFmt fmt; 262 } 263 264 /** The type 'const A' 265 */ 266 static struct RecordInfo { 267 /// A %s 268 SimpleFmt fmt; 269 } 270 } 271 272 /// Attributes for a type. 273 @safe @nogc struct TypeAttr { 274 import std.typecons : Flag; 275 import std.format : FormatSpec; 276 277 Flag!"isConst" isConst; 278 Flag!"isRef" isRef; 279 Flag!"isPtr" isPtr; 280 Flag!"isFuncPtr" isFuncPtr; 281 Flag!"isArray" isArray; 282 Flag!"isDefinition" isDefinition; 283 284 /// Returns: a string range of the attributes 285 auto stringRange() const { 286 import std.range : chain, only; 287 import std.algorithm : filter; 288 289 // dfmt off 290 return chain(only(isConst ? "const" : null), 291 only(isRef ? "ref" : null), 292 only(isPtr ? "ptr" : null), 293 only(isFuncPtr ? "funcPtr" : null), 294 only(isArray ? "array" : null)) 295 .filter!(a => a !is null); 296 // dfmt on 297 } 298 299 /// 300 void toString(Writer, Char)(scope Writer w, FormatSpec!Char fmt = "%s") const { 301 import std.algorithm : joiner, copy; 302 303 // dfmt off 304 this.stringRange 305 .joiner(";") 306 .copy(w); 307 // dfmt on 308 } 309 310 string toString() @safe pure const { 311 import std.exception : assumeUnique; 312 import std.format : FormatSpec; 313 314 char[] buf; 315 buf.reserve(100); 316 auto fmt = FormatSpec!char("%s"); 317 toString((const(char)[] s) { buf ~= s; }, fmt); 318 auto trustedUnique(T)(T t) @trusted { 319 return assumeUnique(t); 320 } 321 322 return trustedUnique(buf); 323 } 324 } 325 326 /** Resolve the canonical type. 327 * 328 * TODO merge with resolvePointeeType. (It wasn't done as of this writing 329 * because I'm not sure they will stay similare enough to allow a merge). 330 * 331 * TODO I think that the resuilt from array/funcPtr/pointee should be checked 332 * if they are a typedef. May be a bug that complicates the result at other 333 * places. 334 * 335 * Params: 336 * LookupT = a type supporting the method "kind" taking a USR and returning a 337 * TypeKind. 338 * type = the type to resolve 339 * attr_ = attributes for the type, depending on the result from the lookup 340 * either this attributes are used or those from the lookup 341 * lookup = see $(D LookupT) 342 * 343 * Returns: TypeKindAttr of the canonical type. 344 */ 345 auto resolveCanonicalType(LookupT)(TypeKind type, TypeAttr attr, LookupT lookup) 346 if (__traits(hasMember, LookupT, "kind")) { 347 import std.algorithm : among; 348 import std.range : only, dropOne; 349 import cpptooling.data : TypeKindAttr; 350 351 auto rval = only(TypeKindAttr.init).dropOne; 352 auto found = typeof(lookup.kind(USRType.init)).init; 353 354 final switch (type.info.kind) with (TypeKind.Info) { 355 case Kind.array: 356 found = lookup.kind(type.info.element); 357 break; 358 case Kind.funcPtr: 359 found = lookup.kind(type.info.pointee); 360 break; 361 case Kind.pointer: 362 found = lookup.kind(type.info.pointee); 363 break; 364 case Kind.typeRef: 365 found = lookup.kind(type.info.canonicalRef); 366 break; 367 case Kind.ctor: 368 case Kind.dtor: 369 case Kind.func: 370 case Kind.funcSignature: 371 case Kind.primitive: 372 case Kind.record: 373 case Kind.simple: 374 rval = only(TypeKindAttr(type, attr)); 375 break; 376 case Kind.null_: 377 break; 378 } 379 380 foreach (item; found) { 381 rval = only(TypeKindAttr(item.get, attr)); 382 } 383 384 return rval; 385 } 386 387 /** Resolve the typeref type. 388 * 389 * TODO merge with resolvePointeeType. (It wasn't done as of this writing 390 * because I'm not sure they will stay similare enough to allow a merge). 391 * 392 * Params: 393 * LookupT = a type supporting taking a USR and returning a TypeKind. 394 * type = the type to resolve 395 * lookup = see $(D LookupT) 396 * 397 * Returns: TypeKind of the canonical type. 398 */ 399 TypeKind resolveTypeRef(LookupT)(const TypeKind type, LookupT lookup) { 400 import std.algorithm : among; 401 import std.range : only, dropOne; 402 import cpptooling.data : TypeKindAttr; 403 404 if (type.info.kind == TypeKind.Info.Kind.typeRef) { 405 foreach (a; lookup(type.info.canonicalRef)) { 406 TypeKind t = a; 407 return t; 408 } 409 } 410 411 return type; 412 } 413 414 /** Resolve the pointe type. 415 * 416 * Params: 417 * LookupT = a type supporting the method "kind" taking a USR as parameter, 418 * returning the result as a TypeKind wrapped in a range. 419 * type = the type to resolve 420 * attr_ = attributes for the type, depending on the result from the lookup 421 * either this attributes are used or those from the lookup 422 * lookup = see $(D LookupT) 423 * 424 * Returns: TypeKindAttr of the pointee type. 425 */ 426 auto resolvePointeeType(LookupT)(TypeKind type, TypeAttr attr, LookupT lookup) 427 if (__traits(hasMember, LookupT, "kind")) { 428 import std.algorithm : among; 429 import std.range : only, dropOne; 430 import cpptooling.data : TypeKindAttr; 431 432 auto rval = only(TypeKindAttr.init).dropOne; 433 auto found = typeof(lookup.kind(USRType.init)).init; 434 435 final switch (type.info.kind) with (TypeKind.Info) { 436 case Kind.array: 437 found = lookup.kind(type.info.element); 438 break; 439 case Kind.funcPtr: 440 found = lookup.kind(type.info.pointee); 441 break; 442 case Kind.pointer: 443 found = lookup.kind(type.info.pointee); 444 break; 445 case Kind.typeRef: 446 case Kind.ctor: 447 case Kind.dtor: 448 case Kind.func: 449 case Kind.funcSignature: 450 case Kind.primitive: 451 case Kind.record: 452 case Kind.simple: 453 rval = only(TypeKindAttr(type, attr)); 454 break; 455 case Kind.null_: 456 break; 457 } 458 459 foreach (item; found) { 460 rval = only(TypeKindAttr(item.get, attr)); 461 } 462 463 return rval; 464 } 465 466 // Test instantiation 467 @safe unittest { 468 TypeKind tk; 469 }