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