aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2010-02-09 22:26:47 +0000
committerDouglas Gregor <dgregor@apple.com>2010-02-09 22:26:47 +0000
commit79a9a3417929e340e84dcbc06ed9c3a277cad959 (patch)
treea3f50e70a16c265f5d7c4eb703d4e84482f43d85
parent41e9066e3f8b46fa10fb868b86a2c9a54526816c (diff)
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
-rw-r--r--include/clang/AST/ASTDiagnostic.h22
-rw-r--r--lib/AST/ASTDiagnostic.cpp266
-rw-r--r--lib/AST/CMakeLists.txt1
-rw-r--r--lib/Sema/Sema.cpp248
4 files changed, 291 insertions, 246 deletions
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<char> &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<ElaboratedType>(Ty)) {
+ QT = cast<ElaboratedType>(Ty)->desugar();
+ continue;
+ }
+
+ // ...or a qualified name type...
+ if (isa<QualifiedNameType>(Ty)) {
+ QT = cast<QualifiedNameType>(Ty)->desugar();
+ continue;
+ }
+
+ // ...or a substituted template type parameter.
+ if (isa<SubstTemplateTypeParmType>(Ty)) {
+ QT = cast<SubstTemplateTypeParmType>(Ty)->desugar();
+ continue;
+ }
+
+ // Don't desugar template specializations.
+ if (isa<TemplateSpecializationType>(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<Class##Type>(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<VectorType>(Underlying))
+ break;
+
+ // Don't desugar through the primary typedef of an anonymous type.
+ if (isa<TagType>(Underlying) && isa<TypedefType>(QT))
+ if (cast<TagType>(Underlying)->getDecl()->getTypedefForAnonDecl() ==
+ cast<TypedefType>(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<char> &Output,
+ void *Cookie) {
+ ASTContext &Context = *static_cast<ASTContext*>(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<void*>(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<NamedDecl*>(Val)->
+ getNameForDiagnostic(S, Context.PrintingPolicy, Qualified);
+ break;
+ }
+ case Diagnostic::ak_nestednamespec: {
+ llvm::raw_string_ostream OS(S);
+ reinterpret_cast<NestedNameSpecifier*>(Val)->print(OS,
+ Context.PrintingPolicy);
+ NeedQuotes = false;
+ break;
+ }
+ case Diagnostic::ak_declcontext: {
+ DeclContext *DC = reinterpret_cast<DeclContext *> (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<TypeDecl>(DC)) {
+ S = ConvertTypeToDiagnosticString(Context,
+ Context.getTypeDeclType(Type),
+ PrevArgs, NumPrevArgs);
+ } else {
+ // FIXME: Get these strings from some localized place
+ NamedDecl *ND = cast<NamedDecl>(DC);
+ if (isa<NamespaceDecl>(ND))
+ S += "namespace ";
+ else if (isa<ObjCMethodDecl>(ND))
+ S += "method ";
+ else if (isa<FunctionDecl>(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<ElaboratedType>(Ty)) {
- QT = cast<ElaboratedType>(Ty)->desugar();
- continue;
- }
-
- // ...or a qualified name type...
- if (isa<QualifiedNameType>(Ty)) {
- QT = cast<QualifiedNameType>(Ty)->desugar();
- continue;
- }
-
- // ...or a substituted template type parameter.
- if (isa<SubstTemplateTypeParmType>(Ty)) {
- QT = cast<SubstTemplateTypeParmType>(Ty)->desugar();
- continue;
- }
-
- // Don't desugar template specializations.
- if (isa<TemplateSpecializationType>(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<Class##Type>(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<VectorType>(Underlying))
- break;
-
- // Don't desugar through the primary typedef of an anonymous type.
- if (isa<TagType>(Underlying) && isa<TypedefType>(QT))
- if (cast<TagType>(Underlying)->getDecl()->getTypedefForAnonDecl() ==
- cast<TypedefType>(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<char> &Output,
- void *Cookie) {
- ASTContext &Context = *static_cast<ASTContext*>(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<void*>(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<NamedDecl*>(Val)->
- getNameForDiagnostic(S, Context.PrintingPolicy, Qualified);
- break;
- }
- case Diagnostic::ak_nestednamespec: {
- llvm::raw_string_ostream OS(S);
- reinterpret_cast<NestedNameSpecifier*>(Val)->print(OS,
- Context.PrintingPolicy);
- NeedQuotes = false;
- break;
- }
- case Diagnostic::ak_declcontext: {
- DeclContext *DC = reinterpret_cast<DeclContext *> (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<TypeDecl>(DC)) {
- S = ConvertTypeToDiagnosticString(Context, Context.getTypeDeclType(Type),
- PrevArgs, NumPrevArgs);
- } else {
- // FIXME: Get these strings from some localized place
- NamedDecl *ND = cast<NamedDecl>(DC);
- if (isa<NamespaceDecl>(ND))
- S += "namespace ";
- else if (isa<ObjCMethodDecl>(ND))
- S += "method ";
- else if (isa<FunctionDecl>(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));