Douglas Gregor | ee75c05 | 2009-05-21 20:55:50 +0000 | [diff] [blame] | 1 | //===--- DocumentXML.cpp - XML document for ASTs --------------------------===// |
| 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | // |
| 10 | // This file implements the XML document class, which provides the means to |
| 11 | // dump out the AST in a XML form that exposes type details and other fields. |
| 12 | // |
| 13 | //===----------------------------------------------------------------------===// |
| 14 | |
| 15 | #include "clang/Frontend/DocumentXML.h" |
| 16 | #include "clang/AST/Decl.h" |
| 17 | #include "clang/AST/DeclCXX.h" |
| 18 | #include "clang/AST/Expr.h" |
| 19 | #include "clang/Basic/SourceManager.h" |
| 20 | #include "llvm/ADT/StringExtras.h" |
| 21 | |
| 22 | namespace clang { |
| 23 | |
| 24 | //--------------------------------------------------------- |
| 25 | struct DocumentXML::NodeXML |
| 26 | { |
| 27 | std::string Name; |
| 28 | NodeXML* Parent; |
| 29 | |
| 30 | NodeXML(const std::string& name, NodeXML* parent) : |
| 31 | Name(name), |
| 32 | Parent(parent) |
| 33 | {} |
| 34 | }; |
| 35 | |
| 36 | //--------------------------------------------------------- |
| 37 | DocumentXML::DocumentXML(const std::string& rootName, llvm::raw_ostream& out) : |
| 38 | Root(new NodeXML(rootName, 0)), |
| 39 | CurrentNode(Root), |
| 40 | Out(out), |
| 41 | Ctx(0), |
| 42 | CurrentIndent(0), |
| 43 | HasCurrentNodeSubNodes(false) |
| 44 | { |
| 45 | Out << "<?xml version=\"1.0\"?>\n<" << rootName; |
| 46 | } |
| 47 | |
| 48 | //--------------------------------------------------------- |
| 49 | DocumentXML::~DocumentXML() |
| 50 | { |
| 51 | assert(CurrentNode == Root && "not completely backtracked"); |
| 52 | delete Root; |
| 53 | } |
| 54 | |
| 55 | //--------------------------------------------------------- |
| 56 | DocumentXML& DocumentXML::addSubNode(const std::string& name) |
| 57 | { |
| 58 | if (!HasCurrentNodeSubNodes) |
| 59 | { |
| 60 | Out << ">\n"; |
| 61 | } |
| 62 | CurrentNode = new NodeXML(name, CurrentNode); |
| 63 | HasCurrentNodeSubNodes = false; |
| 64 | CurrentIndent += 2; |
| 65 | Indent(); |
| 66 | Out << "<" << CurrentNode->Name; |
| 67 | return *this; |
| 68 | } |
| 69 | |
| 70 | //--------------------------------------------------------- |
| 71 | void DocumentXML::Indent() |
| 72 | { |
| 73 | for (int i = 0; i < CurrentIndent; ++i) |
| 74 | Out << ' '; |
| 75 | } |
| 76 | |
| 77 | //--------------------------------------------------------- |
| 78 | DocumentXML& DocumentXML::toParent() |
| 79 | { |
| 80 | assert(CurrentNode != Root && "to much backtracking"); |
| 81 | |
| 82 | if (HasCurrentNodeSubNodes) |
| 83 | { |
| 84 | Indent(); |
| 85 | Out << "</" << CurrentNode->Name << ">\n"; |
| 86 | } |
| 87 | else |
| 88 | { |
| 89 | Out << "/>\n"; |
| 90 | } |
| 91 | NodeXML* NodeToDelete = CurrentNode; |
| 92 | CurrentNode = CurrentNode->Parent; |
| 93 | delete NodeToDelete; |
| 94 | HasCurrentNodeSubNodes = true; |
| 95 | CurrentIndent -= 2; |
| 96 | return *this; |
| 97 | } |
| 98 | |
| 99 | //--------------------------------------------------------- |
| 100 | namespace { |
| 101 | |
| 102 | enum tIdType { ID_NORMAL, ID_FILE, ID_LAST }; |
| 103 | |
| 104 | unsigned getNewId(tIdType idType) |
| 105 | { |
| 106 | static unsigned int idCounts[ID_LAST] = { 0 }; |
| 107 | return ++idCounts[idType]; |
| 108 | } |
| 109 | |
| 110 | //--------------------------------------------------------- |
| 111 | inline std::string getPrefixedId(unsigned uId, tIdType idType) |
| 112 | { |
| 113 | static const char idPrefix[ID_LAST] = { '_', 'f' }; |
| 114 | char buffer[20]; |
| 115 | char* BufPtr = llvm::utohex_buffer(uId, buffer + 20); |
| 116 | *--BufPtr = idPrefix[idType]; |
| 117 | return BufPtr; |
| 118 | } |
| 119 | |
| 120 | //--------------------------------------------------------- |
| 121 | template<class T, class V> |
| 122 | bool addToMap(T& idMap, const V& value, tIdType idType = ID_NORMAL) |
| 123 | { |
| 124 | typename T::iterator i = idMap.find(value); |
| 125 | bool toAdd = i == idMap.end(); |
| 126 | if (toAdd) |
| 127 | { |
| 128 | idMap.insert(typename T::value_type(value, getNewId(idType))); |
| 129 | } |
| 130 | return toAdd; |
| 131 | } |
| 132 | |
| 133 | } // anon NS |
| 134 | |
| 135 | //--------------------------------------------------------- |
| 136 | std::string DocumentXML::escapeString(const char* pStr, std::string::size_type len) |
| 137 | { |
| 138 | std::string value; |
| 139 | value.reserve(len + 1); |
| 140 | char buffer[16]; |
| 141 | for (unsigned i = 0; i < len; ++i) { |
| 142 | switch (char C = pStr[i]) { |
| 143 | default: |
| 144 | if (isprint(C)) |
| 145 | value += C; |
| 146 | else |
| 147 | { |
| 148 | sprintf(buffer, "\\%03o", C); |
| 149 | value += buffer; |
| 150 | } |
| 151 | break; |
| 152 | |
| 153 | case '\n': value += "\\n"; break; |
| 154 | case '\t': value += "\\t"; break; |
| 155 | case '\a': value += "\\a"; break; |
| 156 | case '\b': value += "\\b"; break; |
| 157 | case '\r': value += "\\r"; break; |
| 158 | |
| 159 | case '&': value += "&"; break; |
| 160 | case '<': value += "<"; break; |
| 161 | case '>': value += ">"; break; |
| 162 | case '"': value += """; break; |
| 163 | case '\'': value += "'"; break; |
| 164 | |
| 165 | } |
| 166 | } |
| 167 | return value; |
| 168 | } |
| 169 | |
| 170 | //--------------------------------------------------------- |
| 171 | void DocumentXML::finalize() |
| 172 | { |
| 173 | assert(CurrentNode == Root && "not completely backtracked"); |
| 174 | |
| 175 | addSubNode("ReferenceSection"); |
| 176 | addSubNode("Types"); |
| 177 | |
| 178 | for (XML::IdMap<QualType>::iterator i = Types.begin(), e = Types.end(); i != e; ++i) |
| 179 | { |
| 180 | if (i->first.getCVRQualifiers() != 0) |
| 181 | { |
| 182 | addSubNode("CvQualifiedType"); |
| 183 | addAttribute("id", getPrefixedId(i->second, ID_NORMAL)); |
| 184 | addAttribute("type", getPrefixedId(BasicTypes[i->first.getTypePtr()], ID_NORMAL)); |
| 185 | if (i->first.isConstQualified()) addAttribute("const", "1"); |
| 186 | if (i->first.isVolatileQualified()) addAttribute("volatile", "1"); |
| 187 | if (i->first.isRestrictQualified()) addAttribute("restrict", "1"); |
| 188 | toParent(); |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | for (XML::IdMap<const Type*>::iterator i = BasicTypes.begin(), e = BasicTypes.end(); i != e; ++i) |
| 193 | { |
| 194 | // don't use the get methods as they strip of typedef infos |
| 195 | if (const BuiltinType *BT = dyn_cast<BuiltinType>(i->first)) { |
| 196 | addSubNode("FundamentalType"); |
Douglas Gregor | d249e1d1f | 2009-05-29 20:38:28 +0000 | [diff] [blame] | 197 | addAttribute("name", BT->getName(Ctx->getLangOptions().CPlusPlus)); |
Douglas Gregor | ee75c05 | 2009-05-21 20:55:50 +0000 | [diff] [blame] | 198 | } |
| 199 | else if (const PointerType *PT = dyn_cast<PointerType>(i->first)) { |
| 200 | addSubNode("PointerType"); |
| 201 | addTypeAttribute(PT->getPointeeType()); |
| 202 | } |
| 203 | else if (dyn_cast<FunctionType>(i->first) != 0) { |
| 204 | addSubNode("FunctionType"); |
| 205 | } |
| 206 | else if (const ReferenceType *RT = dyn_cast<ReferenceType>(i->first)) { |
| 207 | addSubNode("ReferenceType"); |
| 208 | addTypeAttribute(RT->getPointeeType()); |
| 209 | } |
| 210 | else if (const TypedefType * TT = dyn_cast<TypedefType>(i->first)) { |
| 211 | addSubNode("Typedef"); |
| 212 | addAttribute("name", TT->getDecl()->getNameAsString()); |
| 213 | addTypeAttribute(TT->getDecl()->getUnderlyingType()); |
| 214 | addContextAttribute(TT->getDecl()->getDeclContext()); |
| 215 | } |
| 216 | else if (const QualifiedNameType *QT = dyn_cast<QualifiedNameType>(i->first)) { |
| 217 | addSubNode("QualifiedNameType"); |
| 218 | addTypeAttribute(QT->getNamedType()); |
| 219 | } |
| 220 | else if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(i->first)) { |
| 221 | addSubNode("ArrayType"); |
| 222 | addAttribute("min", 0); |
| 223 | addAttribute("max", (CAT->getSize() - 1).toString(10, false)); |
| 224 | addTypeAttribute(CAT->getElementType()); |
| 225 | } |
| 226 | else if (const VariableArrayType *VAT = dyn_cast<VariableArrayType>(i->first)) { |
| 227 | addSubNode("VariableArrayType"); |
| 228 | addTypeAttribute(VAT->getElementType()); |
| 229 | } |
| 230 | else if (const TagType *RET = dyn_cast<TagType>(i->first)) { |
| 231 | const TagDecl *tagDecl = RET->getDecl(); |
| 232 | std::string tagKind = tagDecl->getKindName(); |
| 233 | tagKind[0] = std::toupper(tagKind[0]); |
| 234 | addSubNode(tagKind); |
| 235 | addAttribute("name", tagDecl->getNameAsString()); |
| 236 | addContextAttribute(tagDecl->getDeclContext()); |
| 237 | } |
| 238 | else if (const VectorType* VT = dyn_cast<VectorType>(i->first)) { |
| 239 | addSubNode("VectorType"); |
| 240 | addTypeAttribute(VT->getElementType()); |
| 241 | addAttribute("num_elements", VT->getNumElements()); |
| 242 | } |
| 243 | else |
| 244 | { |
| 245 | addSubNode("FIXMEType"); |
| 246 | } |
| 247 | addAttribute("id", getPrefixedId(i->second, ID_NORMAL)); |
| 248 | toParent(); |
| 249 | } |
| 250 | |
| 251 | |
| 252 | toParent().addSubNode("Contexts"); |
| 253 | |
| 254 | for (XML::IdMap<const DeclContext*>::iterator i = Contexts.begin(), e = Contexts.end(); i != e; ++i) |
| 255 | { |
| 256 | addSubNode(i->first->getDeclKindName()); |
| 257 | addAttribute("id", getPrefixedId(i->second, ID_NORMAL)); |
| 258 | if (const NamedDecl *ND = dyn_cast<NamedDecl>(i->first)) { |
| 259 | addAttribute("name", ND->getNameAsString()); |
| 260 | } |
| 261 | if (const TagDecl *TD = dyn_cast<TagDecl>(i->first)) { |
| 262 | addAttribute("type", getPrefixedId(BasicTypes[TD->getTypeForDecl()], ID_NORMAL)); |
| 263 | } |
| 264 | else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(i->first)) { |
| 265 | addAttribute("type", getPrefixedId(BasicTypes[FD->getType()->getAsFunctionType()], ID_NORMAL)); |
| 266 | } |
| 267 | |
| 268 | if (const DeclContext* parent = i->first->getParent()) |
| 269 | { |
| 270 | addContextAttribute(parent); |
| 271 | } |
| 272 | toParent(); |
| 273 | } |
| 274 | |
| 275 | toParent().addSubNode("Files"); |
| 276 | |
| 277 | for (XML::IdMap<std::string>::iterator i = SourceFiles.begin(), e = SourceFiles.end(); i != e; ++i) |
| 278 | { |
| 279 | addSubNode("File"); |
| 280 | addAttribute("id", getPrefixedId(i->second, ID_FILE)); |
| 281 | addAttribute("name", escapeString(i->first.c_str(), i->first.size())); |
| 282 | toParent(); |
| 283 | } |
| 284 | |
| 285 | toParent().toParent(); |
| 286 | |
| 287 | // write the root closing node (which has always subnodes) |
| 288 | Out << "</" << CurrentNode->Name << ">\n"; |
| 289 | } |
| 290 | |
| 291 | //--------------------------------------------------------- |
| 292 | void DocumentXML::addTypeAttribute(const QualType& pType) |
| 293 | { |
| 294 | addTypeRecursively(pType); |
| 295 | addAttribute("type", getPrefixedId(Types[pType], ID_NORMAL)); |
| 296 | } |
| 297 | |
| 298 | //--------------------------------------------------------- |
| 299 | void DocumentXML::addTypeIdAttribute(const Type* pType) |
| 300 | { |
| 301 | addBasicTypeRecursively(pType); |
| 302 | addAttribute("id", getPrefixedId(BasicTypes[pType], ID_NORMAL)); |
| 303 | } |
| 304 | |
| 305 | //--------------------------------------------------------- |
| 306 | void DocumentXML::addTypeRecursively(const QualType& pType) |
| 307 | { |
| 308 | if (addToMap(Types, pType)) |
| 309 | { |
| 310 | addBasicTypeRecursively(pType.getTypePtr()); |
| 311 | // beautifier: a non-qualified type shall be transparent |
| 312 | if (pType.getCVRQualifiers() == 0) |
| 313 | { |
| 314 | Types[pType] = BasicTypes[pType.getTypePtr()]; |
| 315 | } |
| 316 | } |
| 317 | } |
| 318 | |
| 319 | //--------------------------------------------------------- |
| 320 | void DocumentXML::addBasicTypeRecursively(const Type* pType) |
| 321 | { |
| 322 | if (addToMap(BasicTypes, pType)) |
| 323 | { |
| 324 | if (const PointerType *PT = dyn_cast<PointerType>(pType)) { |
| 325 | addTypeRecursively(PT->getPointeeType()); |
| 326 | } |
| 327 | else if (const ReferenceType *RT = dyn_cast<ReferenceType>(pType)) { |
| 328 | addTypeRecursively(RT->getPointeeType()); |
| 329 | } |
| 330 | else if (const TypedefType *TT = dyn_cast<TypedefType>(pType)) { |
| 331 | addTypeRecursively(TT->getDecl()->getUnderlyingType()); |
| 332 | addContextsRecursively(TT->getDecl()->getDeclContext()); |
| 333 | } |
| 334 | else if (const QualifiedNameType *QT = dyn_cast<QualifiedNameType>(pType)) { |
| 335 | addTypeRecursively(QT->getNamedType()); |
| 336 | // FIXME: what to do with NestedNameSpecifier or shall this type be transparent? |
| 337 | } |
| 338 | else if (const ArrayType *AT = dyn_cast<ArrayType>(pType)) { |
| 339 | addTypeRecursively(AT->getElementType()); |
| 340 | // FIXME: doesn't work in the immediate streaming approach |
| 341 | /*if (const VariableArrayType *VAT = dyn_cast<VariableArrayType>(AT)) |
| 342 | { |
| 343 | addSubNode("VariableArraySizeExpression"); |
| 344 | PrintStmt(VAT->getSizeExpr()); |
| 345 | toParent(); |
| 346 | }*/ |
| 347 | } |
| 348 | } |
| 349 | } |
| 350 | |
| 351 | //--------------------------------------------------------- |
| 352 | void DocumentXML::addContextAttribute(const DeclContext *DC, tContextUsage usage) |
| 353 | { |
| 354 | addContextsRecursively(DC); |
| 355 | const char* pAttributeTags[2] = { "context", "id" }; |
| 356 | addAttribute(pAttributeTags[usage], getPrefixedId(Contexts[DC], ID_NORMAL)); |
| 357 | } |
| 358 | |
| 359 | //--------------------------------------------------------- |
| 360 | void DocumentXML::addContextsRecursively(const DeclContext *DC) |
| 361 | { |
| 362 | if (DC != 0 && addToMap(Contexts, DC)) |
| 363 | { |
| 364 | addContextsRecursively(DC->getParent()); |
| 365 | } |
| 366 | } |
| 367 | |
| 368 | //--------------------------------------------------------- |
| 369 | void DocumentXML::addSourceFileAttribute(const std::string& fileName) |
| 370 | { |
| 371 | addToMap(SourceFiles, fileName, ID_FILE); |
| 372 | addAttribute("file", getPrefixedId(SourceFiles[fileName], ID_FILE)); |
| 373 | } |
| 374 | |
| 375 | //--------------------------------------------------------- |
| 376 | PresumedLoc DocumentXML::addLocation(const SourceLocation& Loc) |
| 377 | { |
| 378 | SourceManager& SM = Ctx->getSourceManager(); |
| 379 | SourceLocation SpellingLoc = SM.getSpellingLoc(Loc); |
| 380 | PresumedLoc PLoc; |
| 381 | if (!SpellingLoc.isInvalid()) |
| 382 | { |
| 383 | PLoc = SM.getPresumedLoc(SpellingLoc); |
| 384 | addSourceFileAttribute(PLoc.getFilename()); |
| 385 | addAttribute("line", PLoc.getLine()); |
| 386 | addAttribute("col", PLoc.getColumn()); |
| 387 | } |
| 388 | // else there is no error in some cases (eg. CXXThisExpr) |
| 389 | return PLoc; |
| 390 | } |
| 391 | |
| 392 | //--------------------------------------------------------- |
| 393 | void DocumentXML::addLocationRange(const SourceRange& R) |
| 394 | { |
| 395 | PresumedLoc PStartLoc = addLocation(R.getBegin()); |
| 396 | if (R.getBegin() != R.getEnd()) |
| 397 | { |
| 398 | SourceManager& SM = Ctx->getSourceManager(); |
| 399 | SourceLocation SpellingLoc = SM.getSpellingLoc(R.getEnd()); |
| 400 | if (!SpellingLoc.isInvalid()) |
| 401 | { |
| 402 | PresumedLoc PLoc = SM.getPresumedLoc(SpellingLoc); |
| 403 | if (PStartLoc.isInvalid() || |
| 404 | strcmp(PLoc.getFilename(), PStartLoc.getFilename()) != 0) { |
| 405 | addToMap(SourceFiles, PLoc.getFilename(), ID_FILE); |
| 406 | addAttribute("endfile", PLoc.getFilename()); |
| 407 | addAttribute("endline", PLoc.getLine()); |
| 408 | addAttribute("endcol", PLoc.getColumn()); |
| 409 | } else if (PLoc.getLine() != PStartLoc.getLine()) { |
| 410 | addAttribute("endline", PLoc.getLine()); |
| 411 | addAttribute("endcol", PLoc.getColumn()); |
| 412 | } else { |
| 413 | addAttribute("endcol", PLoc.getColumn()); |
| 414 | } |
| 415 | } |
| 416 | } |
| 417 | } |
| 418 | |
| 419 | //--------------------------------------------------------- |
| 420 | void DocumentXML::PrintFunctionDecl(FunctionDecl *FD) |
| 421 | { |
| 422 | switch (FD->getStorageClass()) { |
| 423 | default: assert(0 && "Unknown storage class"); |
| 424 | case FunctionDecl::None: break; |
| 425 | case FunctionDecl::Extern: addAttribute("storage_class", "extern"); break; |
| 426 | case FunctionDecl::Static: addAttribute("storage_class", "static"); break; |
| 427 | case FunctionDecl::PrivateExtern: addAttribute("storage_class", "__private_extern__"); break; |
| 428 | } |
| 429 | |
| 430 | if (FD->isInline()) |
| 431 | addAttribute("inline", "1"); |
| 432 | |
| 433 | const FunctionType *AFT = FD->getType()->getAsFunctionType(); |
| 434 | addTypeAttribute(AFT->getResultType()); |
| 435 | addBasicTypeRecursively(AFT); |
| 436 | |
| 437 | if (const FunctionProtoType *FT = dyn_cast<FunctionProtoType>(AFT)) { |
| 438 | addAttribute("num_args", FD->getNumParams()); |
| 439 | for (unsigned i = 0, e = FD->getNumParams(); i != e; ++i) { |
| 440 | addSubNode("Argument"); |
| 441 | ParmVarDecl *argDecl = FD->getParamDecl(i); |
| 442 | addAttribute("name", argDecl->getNameAsString()); |
| 443 | addTypeAttribute(FT->getArgType(i)); |
| 444 | addDeclIdAttribute(argDecl); |
| 445 | if (argDecl->getDefaultArg()) |
| 446 | { |
| 447 | addAttribute("default_arg", "1"); |
| 448 | PrintStmt(argDecl->getDefaultArg()); |
| 449 | } |
| 450 | toParent(); |
| 451 | } |
| 452 | |
| 453 | if (FT->isVariadic()) { |
| 454 | addSubNode("Ellipsis").toParent(); |
| 455 | } |
| 456 | } else { |
| 457 | assert(isa<FunctionNoProtoType>(AFT)); |
| 458 | } |
| 459 | } |
| 460 | |
| 461 | //--------------------------------------------------------- |
| 462 | void DocumentXML::addRefAttribute(const NamedDecl* D) |
| 463 | { |
| 464 | // FIXME: in case of CXX inline member functions referring to a member defined |
| 465 | // after the function it needs to be tested, if the ids are already there |
| 466 | // (should work, but I couldn't test it) |
| 467 | if (const DeclContext* DC = dyn_cast<DeclContext>(D)) |
| 468 | { |
| 469 | addAttribute("ref", getPrefixedId(Contexts[DC], ID_NORMAL)); |
| 470 | } |
| 471 | else |
| 472 | { |
| 473 | addAttribute("ref", getPrefixedId(Decls[D], ID_NORMAL)); |
| 474 | } |
| 475 | } |
| 476 | |
| 477 | //--------------------------------------------------------- |
| 478 | void DocumentXML::addDeclIdAttribute(const NamedDecl* D) |
| 479 | { |
| 480 | addToMap(Decls, D); |
| 481 | addAttribute("id", getPrefixedId(Decls[D], ID_NORMAL)); |
| 482 | } |
| 483 | |
| 484 | //--------------------------------------------------------- |
| 485 | void DocumentXML::PrintDecl(Decl *D) |
| 486 | { |
| 487 | addSubNode(D->getDeclKindName()); |
| 488 | addContextAttribute(D->getDeclContext()); |
| 489 | addLocation(D->getLocation()); |
| 490 | if (DeclContext* DC = dyn_cast<DeclContext>(D)) |
| 491 | { |
| 492 | addContextAttribute(DC, CONTEXT_AS_ID); |
| 493 | } |
| 494 | |
| 495 | if (NamedDecl *ND = dyn_cast<NamedDecl>(D)) { |
| 496 | addAttribute("name", ND->getNameAsString()); |
| 497 | |
| 498 | if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { |
| 499 | PrintFunctionDecl(FD); |
| 500 | if (Stmt *Body = FD->getBody(*Ctx)) { |
| 501 | addSubNode("Body"); |
| 502 | PrintStmt(Body); |
| 503 | toParent(); |
| 504 | } |
| 505 | } else if (RecordDecl *RD = dyn_cast<RecordDecl>(D)) { |
| 506 | addBasicTypeRecursively(RD->getTypeForDecl()); |
| 507 | addAttribute("type", getPrefixedId(BasicTypes[RD->getTypeForDecl()], ID_NORMAL)); |
| 508 | if (!RD->isDefinition()) |
| 509 | { |
| 510 | addAttribute("forward", "1"); |
| 511 | } |
| 512 | |
| 513 | for (RecordDecl::field_iterator i = RD->field_begin(*Ctx), e = RD->field_end(*Ctx); i != e; ++i) |
| 514 | { |
| 515 | PrintDecl(*i); |
| 516 | } |
| 517 | } else if (EnumDecl *ED = dyn_cast<EnumDecl>(D)) { |
| 518 | const QualType& enumType = ED->getIntegerType(); |
| 519 | if (!enumType.isNull()) |
| 520 | { |
| 521 | addTypeAttribute(enumType); |
| 522 | for (EnumDecl::enumerator_iterator i = ED->enumerator_begin(*Ctx), e = ED->enumerator_end(*Ctx); i != e; ++i) |
| 523 | { |
| 524 | PrintDecl(*i); |
| 525 | } |
| 526 | } |
| 527 | } else if (EnumConstantDecl* ECD = dyn_cast<EnumConstantDecl>(D)) { |
| 528 | addTypeAttribute(ECD->getType()); |
| 529 | addAttribute("value", ECD->getInitVal().toString(10, true)); |
| 530 | if (ECD->getInitExpr()) |
| 531 | { |
| 532 | PrintStmt(ECD->getInitExpr()); |
| 533 | } |
| 534 | } else if (FieldDecl *FdD = dyn_cast<FieldDecl>(D)) { |
| 535 | addTypeAttribute(FdD->getType()); |
| 536 | addDeclIdAttribute(ND); |
| 537 | if (FdD->isMutable()) |
| 538 | addAttribute("mutable", "1"); |
| 539 | if (FdD->isBitField()) |
| 540 | { |
| 541 | addAttribute("bitfield", "1"); |
| 542 | PrintStmt(FdD->getBitWidth()); |
| 543 | } |
| 544 | } else if (TypedefDecl *TD = dyn_cast<TypedefDecl>(D)) { |
| 545 | addTypeIdAttribute(Ctx->getTypedefType(TD).getTypePtr()); |
| 546 | addTypeAttribute(TD->getUnderlyingType()); |
| 547 | } else if (ValueDecl *VD = dyn_cast<ValueDecl>(D)) { |
| 548 | addTypeAttribute(VD->getType()); |
| 549 | addDeclIdAttribute(ND); |
| 550 | |
| 551 | VarDecl *V = dyn_cast<VarDecl>(VD); |
| 552 | if (V && V->getStorageClass() != VarDecl::None) |
| 553 | { |
| 554 | addAttribute("storage_class", VarDecl::getStorageClassSpecifierString(V->getStorageClass())); |
| 555 | } |
| 556 | |
| 557 | if (V && V->getInit()) |
| 558 | { |
| 559 | PrintStmt(V->getInit()); |
| 560 | } |
| 561 | } |
| 562 | } else if (LinkageSpecDecl* LSD = dyn_cast<LinkageSpecDecl>(D)) { |
| 563 | switch (LSD->getLanguage()) |
| 564 | { |
| 565 | case LinkageSpecDecl::lang_c: addAttribute("lang", "C"); break; |
| 566 | case LinkageSpecDecl::lang_cxx: addAttribute("lang", "CXX"); break; |
| 567 | default: assert(0 && "Unexpected lang id"); |
| 568 | } |
Douglas Gregor | 4fe0c8e | 2009-05-30 00:08:05 +0000 | [diff] [blame^] | 569 | } else if (isa<FileScopeAsmDecl>(D)) { |
| 570 | // FIXME: Implement this |
Douglas Gregor | ee75c05 | 2009-05-21 20:55:50 +0000 | [diff] [blame] | 571 | } else { |
| 572 | assert(0 && "Unexpected decl"); |
| 573 | } |
| 574 | toParent(); |
| 575 | } |
| 576 | |
| 577 | //--------------------------------------------------------- |
| 578 | } // NS clang |
| 579 | |