From: Douglas Gregor Date: Tue, 9 Feb 2010 22:26:47 +0000 (+0000) Subject: Move the diagnostic argument formatting function out of Sema and make X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=79a9a3417929e340e84dcbc06ed9c3a277cad959;p=clang Move the diagnostic argument formatting function out of Sema and make it available within the AST library, of which Sema is one client. No functionality change. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@95701 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ASTDiagnostic.h b/include/clang/AST/ASTDiagnostic.h index abd36f7e5f..2d31491346 100644 --- a/include/clang/AST/ASTDiagnostic.h +++ b/include/clang/AST/ASTDiagnostic.h @@ -1,4 +1,4 @@ -//===--- DiagnosticAST.h - Diagnostics for the AST library ------*- C++ -*-===// +//===--- ASTDiagnostic.h - Diagnostics for the AST library ------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -22,6 +22,26 @@ namespace clang { NUM_BUILTIN_AST_DIAGNOSTICS }; } // end namespace diag + + /// \brief Diagnostic argument formatting function for diagnostics that + /// involve AST nodes. + /// + /// This function formats diagnostic arguments for various AST nodes, + /// including types, declaration names, nested name specifiers, and + /// declaration contexts, into strings that can be printed as part of + /// diagnostics. It is meant to be used as the argument to + /// \c Diagnostic::SetArgToStringFn(), where the cookie is an \c ASTContext + /// pointer. + void 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); } // end namespace clang #endif diff --git a/lib/AST/ASTDiagnostic.cpp b/lib/AST/ASTDiagnostic.cpp new file mode 100644 index 0000000000..7402b7dda4 --- /dev/null +++ b/lib/AST/ASTDiagnostic.cpp @@ -0,0 +1,266 @@ +//===--- 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 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('\''); +} diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index 364f7a5ecd..e5bd9b7722 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -5,6 +5,7 @@ add_clang_library(clangAST ASTConsumer.cpp ASTContext.cpp ASTImporter.cpp + ASTDiagnostic.cpp AttrImpl.cpp CXXInheritance.cpp Decl.cpp diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 903b987080..e9a1d9c5fc 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -19,257 +19,14 @@ #include "llvm/ADT/APFloat.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/ASTDiagnostic.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/Lex/Preprocessor.h" #include "clang/Basic/PartialDiagnostic.h" #include "clang/Basic/TargetInfo.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 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; -} -/// ConvertQualTypeToStringFn - This function is used to pretty print the -/// specified QualType as a string in diagnostics. -static void ConvertArgToStringFn(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('\''); -} - - static inline RecordDecl *CreateStructDecl(ASTContext &C, const char *Name) { if (C.getLangOptions().CPlusPlus) return CXXRecordDecl::Create(C, TagDecl::TK_struct, @@ -371,7 +128,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, FieldCollector.reset(new CXXFieldCollector()); // Tell diagnostics how to render things from the AST library. - PP.getDiagnostics().SetArgToStringFn(ConvertArgToStringFn, &Context); + PP.getDiagnostics().SetArgToStringFn(&FormatASTNodeDiagnosticArgument, + &Context); ExprEvalContexts.push_back( ExpressionEvaluationContextRecord(PotentiallyEvaluated, 0));