1 /** 2 Date: 2015-2017, 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 module cpptooling.data.kind_type; 9 10 import std.conv : to; 11 import std..string : format; 12 import std.traits; 13 import std.typecons : Tuple, Nullable, Flag; 14 import logger = std.experimental.logger; 15 16 import cpptooling.data.symbol.types : USRType; 17 import cpptooling.data.type : LocationTag; 18 19 public import cpptooling.data.kind; 20 21 struct TypeKindAttr { 22 TypeKind kind; 23 TypeAttr attr; 24 } 25 26 struct TypeResult { 27 TypeKindAttr type; 28 LocationTag location; 29 } 30 31 struct TypeResults { 32 TypeResult primary; 33 TypeResult[] extra; 34 } 35 36 /** Merge rhs into lhs. 37 */ 38 ref TypeResults mergeExtra(ref return TypeResults lhs, const ref TypeResults rhs) { 39 lhs.extra ~= rhs.extra; 40 return lhs; 41 } 42 43 /// Pretty loggning with indentation. 44 void logTypeAttr(const ref TypeAttr attr, const uint indent = 0, 45 in uint extra_space = 0, in string func = __FUNCTION__, in uint line = __LINE__) @safe pure { 46 import std.array : array; 47 import std.range : repeat; 48 import logger = std.experimental.logger; 49 import clang.info; 50 51 // dfmt off 52 debug { 53 string indent_ = repeat(' ', indent + extra_space).array(); 54 logger.logf!(-1, "", "", "", "") 55 (logger.LogLevel.trace, 56 "%d%s %s [%s:%d]", 57 indent, 58 indent_, 59 attr, 60 func, 61 line); 62 } 63 // dfmt on 64 } 65 66 /// Pretty loggning with indentation. 67 void logTypeResult(ref const(TypeResult) result, in uint indent, 68 in string func = __FUNCTION__, in uint line = __LINE__) @safe pure nothrow { 69 import std.array : array; 70 import std.conv : to; 71 import std.range : repeat; 72 import logger = std.experimental.logger; 73 74 // dfmt off 75 try { 76 debug { 77 string indent_ = repeat(' ', indent).array(); 78 string extra; 79 switch (result.type.kind.info.kind) with (TypeKind.Info) { 80 case Kind.typeRef: 81 extra = "|ex ref:" ~ result.type.kind.info.typeRef ~ "|ex canonical:" ~ result.type.kind.info.canonicalRef; 82 break; 83 case Kind.funcPtr: 84 case Kind.pointer: 85 extra = "|ex usr:" ~ result.type.kind.info.pointee; 86 break; 87 case Kind.array: 88 extra = "|ex elem:" ~ result.type.kind.info.element; 89 break; 90 default: 91 } 92 93 logger.logf!(-1, "", "", "", "") 94 (logger.LogLevel.trace, 95 "%d%s %s|%s|repr:%s|loc:%s %s|usr:%s|%s%s [%s:%d]", 96 indent, 97 indent_, 98 to!string(result.type.kind.info.kind), 99 result.type.kind.splitTypeId, 100 result.type.toStringDecl("x"), 101 (result.location.kind == LocationTag.Kind.loc) ? (result.location.file.length == 0 ? "no" : "yes") : "noloc", 102 (result.type.attr.isDefinition ? "def" : "decl"), 103 result.type.kind.usr, 104 result.type.attr, 105 extra, 106 func, 107 line); 108 109 switch (result.type.kind.info.kind) with (TypeKind.Info) { 110 case Kind.func: 111 foreach (r; result.type.kind.info.params) { 112 logTypeAttr(r.attr, indent, 1, func, line); 113 } 114 break; 115 case Kind.pointer: 116 foreach (r; result.type.kind.info.attrs) { 117 logTypeAttr(r, indent, 1, func, line); 118 } 119 break; 120 default: 121 } 122 } 123 } catch (Exception ex) { 124 } 125 // dfmt on 126 } 127 128 /// Pretty loggning with indentation. 129 void logTypeResult(ref const(TypeResults) results, in uint indent = 0, 130 in string func = __FUNCTION__, in uint line = __LINE__) @safe pure nothrow { 131 import std.range : chain, only; 132 133 // dfmt off 134 debug { 135 foreach (const ref result; chain(only(results.primary), results.extra)) { 136 logTypeResult(result, indent, func, line); 137 } 138 } 139 // dfmt on 140 } 141 142 /// Pretty loggning with indentation. 143 void logTypeResult(ref const(Nullable!TypeResults) results, in uint indent = 0, 144 in string func = __FUNCTION__, in uint line = __LINE__) @safe pure nothrow { 145 debug { 146 if (!results.isNull) { 147 logTypeResult(results.get, indent, func, line); 148 } 149 } 150 } 151 152 //TODO remove, this is not good. keep it focused on SimleInfo. 153 TypeKindAttr makeSimple(string txt, TypeAttr attr = TypeAttr.init) pure @safe nothrow { 154 import cpptooling.data : SimpleFmt, TypeId; 155 156 TypeKind t; 157 t.info = TypeKind.SimpleInfo(SimpleFmt(TypeId(txt))); 158 159 return TypeKindAttr(t, attr); 160 } 161 162 private auto toCvPtrQ(T)(ref T app, const(TypeAttr)[] attrs) { 163 import cpptooling.data; 164 165 foreach (attr; attrs) { 166 // TODO merge isPtr/isRef to an enum in the data structure for 167 // attributes 168 // should be either, never both 169 assert(!(attr.isPtr && attr.isRef)); 170 171 CvPtrQ a; 172 a.cvQual = attr.isConst ? CvQ.const_ : CvQ(); 173 if (attr.isPtr) 174 a.ptrQual = PtrQ.ptr; 175 else if (attr.isRef) 176 a.ptrQual = PtrQ.ref_; 177 178 app.put(a); 179 } 180 } 181 182 /** Combine type attributes, kind and identifier to produce a declaration. 183 * TODO reconsider the function name. 184 * - Don't encode the return type in the name (?) 185 * - Is it really a Decl-> declaration? Maybe more appropriate would be 186 * "merge", of type and attributes? 187 */ 188 auto toStringDecl(const TypeKind t, const TypeAttr ta, string id) @safe pure { 189 import std.array : appender, Appender; 190 import cpptooling.data; 191 192 static void oneArg(T)(ref Appender!string app, ref T fmt, ref const TypeAttr ta, DeclId id) { 193 fmt.toString(app, ta.isConst ? CvQ.const_ : CvQ(), id); 194 } 195 196 static void twoArg(T0, T1)(ref Appender!string app, ref T0 fmt, 197 ref const TypeAttr ta, DeclId id, T1 data1) { 198 fmt.toString(app, app, ta.isConst ? CvQ.const_ : CvQ(), data1, id); 199 } 200 201 auto txt = appender!string(); 202 203 // TODO sort them by alphabetic order 204 205 final switch (t.info.kind) with (TypeKind.Info) { 206 case Kind.primitive: 207 auto info = cast(const TypeKind.PrimitiveInfo) t.info; 208 oneArg(txt, info.fmt, ta, DeclId(id)); 209 break; 210 case Kind.record: 211 auto info = cast(const TypeKind.RecordInfo) t.info; 212 oneArg(txt, info.fmt, ta, DeclId(id)); 213 break; 214 case Kind.simple: 215 auto info = cast(const TypeKind.SimpleInfo) t.info; 216 oneArg(txt, info.fmt, ta, DeclId(id)); 217 break; 218 case Kind.typeRef: 219 auto info = cast(const TypeKind.TypeRefInfo) t.info; 220 oneArg(txt, info.fmt, ta, DeclId(id)); 221 break; 222 case Kind.array: 223 auto info = cast(const TypeKind.ArrayInfo) t.info; 224 ArraySize sz; 225 226 foreach (a; info.indexes) { 227 if (a.isNull) { 228 sz ~= ArraySize.Size(); 229 } else { 230 sz ~= ArraySize.Size(ArraySize.Kind.const_, a.get); 231 } 232 } 233 234 info.fmt.toString(txt, txt, ta.isConst ? CvQ.const_ : CvQ(), DeclId(id), sz); 235 break; 236 case Kind.func: 237 auto info = cast(const TypeKind.FuncInfo) t.info; 238 info.fmt.toString(txt, txt, DeclId(id)); 239 break; 240 case Kind.funcSignature: 241 auto info = cast(const TypeKind.FuncSignatureInfo) t.info; 242 info.fmt.toString(txt, txt); 243 break; 244 case Kind.funcPtr: 245 auto ptrs = appender!(CvPtrQ[])(); 246 toCvPtrQ(ptrs, t.info.attrs); 247 248 auto info = cast(const TypeKind.FuncPtrInfo) t.info; 249 twoArg(txt, info.fmt, ta, DeclId(id), ptrs.data); 250 break; 251 case Kind.pointer: 252 auto ptrs = appender!(CvPtrQ[])(); 253 toCvPtrQ(ptrs, t.info.attrs); 254 255 auto info = cast(const TypeKind.PointerInfo) t.info; 256 twoArg(txt, info.fmt, ta, DeclId(id), ptrs.data); 257 break; 258 case Kind.ctor: 259 auto info = cast(const TypeKind.CtorInfo) t.info; 260 info.fmt.toString(txt, DeclId(id)); 261 break; 262 case Kind.dtor: 263 auto info = cast(const TypeKind.DtorInfo) t.info; 264 info.fmt.toString(txt, DeclId(id)); 265 break; 266 case Kind.null_: 267 debug { 268 logger.error("Type is null. Identifier ", id); 269 } 270 txt.put(id); 271 break; 272 } 273 274 return txt.data; 275 } 276 277 /// ditto 278 auto toStringDecl(const TypeKind t, const TypeAttr ta) { 279 import std..string : strip; 280 281 // TODO consider changing the implementation of to NOT take an id. 282 // Would avoid the strip.... 283 return t.toStringDecl(ta, "").strip; 284 } 285 286 /// if a type can be cast to a TypeKindAttr. 287 auto toStringDecl(T)(const T value, string id) 288 if (is(typeof(cast(TypeKindAttr) value) == TypeKindAttr)) { 289 return (cast(TypeKindAttr) value).kind.toStringDecl(value.attr, id); 290 } 291 292 /// ditto 293 auto toStringDecl(T)(const T value) 294 if (is(typeof(cast(TypeKindAttr) value) == TypeKindAttr)) { 295 return (cast(TypeKindAttr) value).kind.toStringDecl(value.attr); 296 } 297 298 /** Split the TypeId from the formatter in a Left/Right. 299 * 300 * TODO duplicate code between this and toStringDecl. 301 */ 302 auto splitTypeId(ref const TypeKind t) @safe pure { 303 import std.array : appender, Appender; 304 import cpptooling.data; 305 306 TypeIdLR rval; 307 308 final switch (t.info.kind) with (TypeKind.Info) { 309 case Kind.primitive: 310 auto info = cast(const TypeKind.PrimitiveInfo) t.info; 311 rval.left = info.fmt.typeId; 312 break; 313 case Kind.record: 314 auto info = cast(const TypeKind.RecordInfo) t.info; 315 rval.left = info.fmt.typeId; 316 break; 317 case Kind.simple: 318 auto info = cast(const TypeKind.SimpleInfo) t.info; 319 rval.left = info.fmt.typeId; 320 break; 321 case Kind.typeRef: 322 auto info = cast(const TypeKind.TypeRefInfo) t.info; 323 rval.left = info.fmt.typeId; 324 break; 325 case Kind.array: 326 auto info = cast(const TypeKind.ArrayInfo) t.info; 327 ArraySize sz; 328 329 foreach (a; info.indexes) { 330 if (a.isNull) { 331 sz ~= ArraySize.Size(); 332 } else { 333 sz ~= ArraySize.Size(ArraySize.Kind.const_, a.get); 334 } 335 } 336 337 auto wl = appender!string(); 338 auto wr = appender!string(); 339 info.fmt.toString(wl, wr, CvQ(), DeclId(null), sz); 340 rval = TypeIdLR(Left(wl.data), Right(wr.data)); 341 break; 342 case Kind.funcSignature: 343 auto info = cast(const TypeKind.FuncSignatureInfo) t.info; 344 auto wl = appender!string(); 345 auto wr = appender!string(); 346 info.fmt.toString(wl, wr); 347 rval = TypeIdLR(Left(wl.data), Right(wr.data)); 348 break; 349 case Kind.func: 350 auto info = cast(const TypeKind.FuncInfo) t.info; 351 auto wl = appender!string(); 352 auto wr = appender!string(); 353 info.fmt.toString(wl, wr, DeclId(null)); 354 rval = TypeIdLR(Left(wl.data), Right(wr.data)); 355 break; 356 case Kind.funcPtr: 357 auto ptrs = appender!(CvPtrQ[])(); 358 toCvPtrQ(ptrs, t.info.attrs); 359 360 auto info = cast(const TypeKind.FuncPtrInfo) t.info; 361 auto wl = appender!string(); 362 auto wr = appender!string(); 363 info.fmt.toString(wl, wr, CvQ(), ptrs.data, DeclId(null)); 364 rval = TypeIdLR(Left(wl.data), Right(wr.data)); 365 break; 366 case Kind.pointer: 367 auto ptrs = appender!(CvPtrQ[])(); 368 toCvPtrQ(ptrs, t.info.attrs); 369 370 auto info = cast(const TypeKind.PointerInfo) t.info; 371 auto wl = appender!string(); 372 auto wr = appender!string(); 373 info.fmt.toString(wl, wr, CvQ(), ptrs.data, DeclId(null)); 374 rval = TypeIdLR(Left(wl.data), Right(wr.data)); 375 break; 376 case Kind.ctor: 377 // have no TypeId 378 break; 379 case Kind.dtor: 380 // have no TypeId 381 break; 382 case Kind.null_: 383 debug logger.error("Type is null"); 384 break; 385 } 386 387 return rval; 388 } 389 390 /// ditto 391 auto splitTypeId(ref const TypeKind t, const uint indent = 0) @safe pure 392 out (result) { 393 import dextool.logger : trace; 394 import std.conv : to; 395 396 debug { 397 trace(result.to!string(), indent); 398 } 399 } 400 body { 401 return splitTypeId(t); 402 }