//===--- ASTDiagnostic.cpp - Diagnostic Printing Hooks for AST Nodes ------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file implements a diagnostic formatting hook for AST elements. // //===----------------------------------------------------------------------===// #include "clang/AST/ASTDiagnostic.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Type.h" #include "llvm/Support/raw_ostream.h" using namespace clang; /// Determines whether we should have an a.k.a. clause when /// pretty-printing a type. There are three main criteria: /// /// 1) Some types provide very minimal sugar that doesn't impede the /// user's understanding --- for example, elaborated type /// specifiers. If this is all the sugar we see, we don't want an /// a.k.a. clause. /// 2) Some types are technically sugared but are much more familiar /// when seen in their sugared form --- for example, va_list, /// vector types, and the magic Objective C types. We don't /// want to desugar these, even if we do produce an a.k.a. clause. /// 3) Some types may have already been desugared previously in this diagnostic. /// if this is the case, doing another "aka" would just be clutter. /// static bool ShouldAKA(ASTContext &Context, QualType QT, const Diagnostic::ArgumentValue *PrevArgs, unsigned NumPrevArgs, QualType &DesugaredQT) { QualType InputTy = QT; bool AKA = false; QualifierCollector Qc; while (true) { const Type *Ty = Qc.strip(QT); // Don't aka just because we saw an elaborated type... if (isa(Ty)) { QT = cast(Ty)->desugar(); continue; } // ...or a qualified name type... if (isa(Ty)) { QT = cast(Ty)->desugar(); continue; } // ...or an injected class name... if (isa(Ty)) { QT = cast(Ty)->desugar(); continue; } // ...or a substituted template type parameter. if (isa(Ty)) { QT = cast(Ty)->desugar(); continue; } // Don't desugar template specializations. if (isa(Ty)) break; // Don't desugar magic Objective-C types. if (QualType(Ty,0) == Context.getObjCIdType() || QualType(Ty,0) == Context.getObjCClassType() || QualType(Ty,0) == Context.getObjCSelType() || QualType(Ty,0) == Context.getObjCProtoType()) break; // Don't desugar va_list. if (QualType(Ty,0) == Context.getBuiltinVaListType()) break; // Otherwise, do a single-step desugar. QualType Underlying; bool IsSugar = false; switch (Ty->getTypeClass()) { #define ABSTRACT_TYPE(Class, Base) #define TYPE(Class, Base) \ case Type::Class: { \ const Class##Type *CTy = cast(Ty); \ if (CTy->isSugared()) { \ IsSugar = true; \ Underlying = CTy->desugar(); \ } \ break; \ } #include "clang/AST/TypeNodes.def" } // If it wasn't sugared, we're done. if (!IsSugar) break; // If the desugared type is a vector type, we don't want to expand // it, it will turn into an attribute mess. People want their "vec4". if (isa(Underlying)) break; // Don't desugar through the primary typedef of an anonymous type. if (isa(Underlying) && isa(QT)) if (cast(Underlying)->getDecl()->getTypedefForAnonDecl() == cast(QT)->getDecl()) break; // Otherwise, we're tearing through something opaque; note that // we'll eventually need an a.k.a. clause and keep going. AKA = true; QT = Underlying; continue; } // If we never tore through opaque sugar, don't print aka. if (!AKA) return false; // If we did, check to see if we already desugared this type in this // diagnostic. If so, don't do it again. for (unsigned i = 0; i != NumPrevArgs; ++i) { // TODO: Handle ak_declcontext case. if (PrevArgs[i].first == Diagnostic::ak_qualtype) { void *Ptr = (void*)PrevArgs[i].second; QualType PrevTy(QualType::getFromOpaquePtr(Ptr)); if (PrevTy == InputTy) return false; } } DesugaredQT = Qc.apply(QT); return true; } /// \brief Convert the given type to a string suitable for printing as part of /// a diagnostic. /// /// \param Context the context in which the type was allocated /// \param Ty the type to print static std::string ConvertTypeToDiagnosticString(ASTContext &Context, QualType Ty, const Diagnostic::ArgumentValue *PrevArgs, unsigned NumPrevArgs) { // FIXME: Playing with std::string is really slow. std::string S = Ty.getAsString(Context.PrintingPolicy); // Consider producing an a.k.a. clause if removing all the direct // sugar gives us something "significantly different". QualType DesugaredTy; if (ShouldAKA(Context, Ty, PrevArgs, NumPrevArgs, DesugaredTy)) { S = "'"+S+"' (aka '"; S += DesugaredTy.getAsString(Context.PrintingPolicy); S += "')"; return S; } S = "'" + S + "'"; return S; } void clang::FormatASTNodeDiagnosticArgument(Diagnostic::ArgumentKind Kind, intptr_t Val, const char *Modifier, unsigned ModLen, const char *Argument, unsigned ArgLen, const Diagnostic::ArgumentValue *PrevArgs, unsigned NumPrevArgs, llvm::SmallVectorImpl &Output, void *Cookie) { ASTContext &Context = *static_cast(Cookie); std::string S; bool NeedQuotes = true; switch (Kind) { default: assert(0 && "unknown ArgumentKind"); case Diagnostic::ak_qualtype: { assert(ModLen == 0 && ArgLen == 0 && "Invalid modifier for QualType argument"); QualType Ty(QualType::getFromOpaquePtr(reinterpret_cast(Val))); S = ConvertTypeToDiagnosticString(Context, Ty, PrevArgs, NumPrevArgs); NeedQuotes = false; break; } case Diagnostic::ak_declarationname: { DeclarationName N = DeclarationName::getFromOpaqueInteger(Val); S = N.getAsString(); if (ModLen == 9 && !memcmp(Modifier, "objcclass", 9) && ArgLen == 0) S = '+' + S; else if (ModLen == 12 && !memcmp(Modifier, "objcinstance", 12) && ArgLen==0) S = '-' + S; else assert(ModLen == 0 && ArgLen == 0 && "Invalid modifier for DeclarationName argument"); break; } case Diagnostic::ak_nameddecl: { bool Qualified; if (ModLen == 1 && Modifier[0] == 'q' && ArgLen == 0) Qualified = true; else { assert(ModLen == 0 && ArgLen == 0 && "Invalid modifier for NamedDecl* argument"); Qualified = false; } reinterpret_cast(Val)-> getNameForDiagnostic(S, Context.PrintingPolicy, Qualified); break; } case Diagnostic::ak_nestednamespec: { llvm::raw_string_ostream OS(S); reinterpret_cast(Val)->print(OS, Context.PrintingPolicy); NeedQuotes = false; break; } case Diagnostic::ak_declcontext: { DeclContext *DC = reinterpret_cast (Val); assert(DC && "Should never have a null declaration context"); if (DC->isTranslationUnit()) { // FIXME: Get these strings from some localized place if (Context.getLangOptions().CPlusPlus) S = "the global namespace"; else S = "the global scope"; } else if (TypeDecl *Type = dyn_cast(DC)) { S = ConvertTypeToDiagnosticString(Context, Context.getTypeDeclType(Type), PrevArgs, NumPrevArgs); } else { // FIXME: Get these strings from some localized place NamedDecl *ND = cast(DC); if (isa(ND)) S += "namespace "; else if (isa(ND)) S += "method "; else if (isa(ND)) S += "function "; S += "'"; ND->getNameForDiagnostic(S, Context.PrintingPolicy, true); S += "'"; } NeedQuotes = false; break; } } if (NeedQuotes) Output.push_back('\''); Output.append(S.begin(), S.end()); if (NeedQuotes) Output.push_back('\''); }