diff options
-rw-r--r-- | include/clang/AST/ASTDiagnostic.h | 22 | ||||
-rw-r--r-- | include/clang/Basic/Diagnostic.h | 22 | ||||
-rw-r--r-- | lib/AST/ASTDiagnostic.cpp | 74 | ||||
-rw-r--r-- | lib/Basic/Diagnostic.cpp | 14 | ||||
-rw-r--r-- | test/Misc/diag-aka-types.cpp | 38 |
5 files changed, 129 insertions, 41 deletions
diff --git a/include/clang/AST/ASTDiagnostic.h b/include/clang/AST/ASTDiagnostic.h index 1cb803a339..70a548d4e9 100644 --- a/include/clang/AST/ASTDiagnostic.h +++ b/include/clang/AST/ASTDiagnostic.h @@ -33,16 +33,18 @@ namespace clang { /// 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); + 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, + llvm::SmallVectorImpl<intptr_t> &QualTypeVals); } // end namespace clang #endif diff --git a/include/clang/Basic/Diagnostic.h b/include/clang/Basic/Diagnostic.h index 3b3ee4d4b0..6f72976bfc 100644 --- a/include/clang/Basic/Diagnostic.h +++ b/include/clang/Basic/Diagnostic.h @@ -277,13 +277,15 @@ private: /// can use this information to avoid redundancy across arguments. /// /// This is a hack to avoid a layering violation between libbasic and libsema. - typedef void (*ArgToStringFnTy)(ArgumentKind Kind, intptr_t Val, - const char *Modifier, unsigned ModifierLen, - const char *Argument, unsigned ArgumentLen, - const ArgumentValue *PrevArgs, - unsigned NumPrevArgs, - llvm::SmallVectorImpl<char> &Output, - void *Cookie); + typedef void (*ArgToStringFnTy)( + ArgumentKind Kind, intptr_t Val, + const char *Modifier, unsigned ModifierLen, + const char *Argument, unsigned ArgumentLen, + const ArgumentValue *PrevArgs, + unsigned NumPrevArgs, + llvm::SmallVectorImpl<char> &Output, + void *Cookie, + llvm::SmallVectorImpl<intptr_t> &QualTypeVals); void *ArgToStringCookie; ArgToStringFnTy ArgToStringFn; @@ -465,9 +467,11 @@ public: const char *Modifier, unsigned ModLen, const char *Argument, unsigned ArgLen, const ArgumentValue *PrevArgs, unsigned NumPrevArgs, - llvm::SmallVectorImpl<char> &Output) const { + llvm::SmallVectorImpl<char> &Output, + llvm::SmallVectorImpl<intptr_t> &QualTypeVals) const { ArgToStringFn(Kind, Val, Modifier, ModLen, Argument, ArgLen, - PrevArgs, NumPrevArgs, Output, ArgToStringCookie); + PrevArgs, NumPrevArgs, Output, ArgToStringCookie, + QualTypeVals); } void SetArgToStringFn(ArgToStringFnTy Fn, void *Cookie) { diff --git a/lib/AST/ASTDiagnostic.cpp b/lib/AST/ASTDiagnostic.cpp index 16d2f85360..7c91b5cb7a 100644 --- a/lib/AST/ASTDiagnostic.cpp +++ b/lib/AST/ASTDiagnostic.cpp @@ -129,7 +129,7 @@ break; \ /// \brief Convert the given type to a string suitable for printing as part of /// a diagnostic. /// -/// There are three main criteria when determining whether we should have an +/// There are four main criteria when determining whether we should have an /// a.k.a. clause when pretty-printing a type: /// /// 1) Some types provide very minimal sugar that doesn't impede the @@ -142,15 +142,44 @@ break; \ /// 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. +/// 4) Two different types within the same diagnostic have the same output +/// string. In this case, force an a.k.a with the desugared type when +/// doing so will provide additional information. /// /// \param Context the context in which the type was allocated /// \param Ty the type to print +/// \param QualTypeVals pointer values to QualTypes which are used in the +/// diagnostic message static std::string ConvertTypeToDiagnosticString(ASTContext &Context, QualType Ty, const Diagnostic::ArgumentValue *PrevArgs, - unsigned NumPrevArgs) { + unsigned NumPrevArgs, + llvm::SmallVectorImpl<intptr_t> &QualTypeVals) { // FIXME: Playing with std::string is really slow. + bool ForceAKA = false; + QualType CanTy = Ty.getCanonicalType(); std::string S = Ty.getAsString(Context.PrintingPolicy); + std::string CanS = CanTy.getAsString(Context.PrintingPolicy); + + for (llvm::SmallVectorImpl<intptr_t>::iterator I = QualTypeVals.begin(), + E = QualTypeVals.end(); I != E; ++I) { + QualType CompareTy = + QualType::getFromOpaquePtr(reinterpret_cast<void*>(*I)); + if (CompareTy == Ty) + continue; // Same types + QualType CompareCanTy = CompareTy.getCanonicalType(); + if (CompareCanTy == CanTy) + continue; // Same canonical types + std::string CompareS = CompareTy.getAsString(Context.PrintingPolicy); + if (CompareS != S) + continue; // Original strings are different + std::string CompareCanS = CompareCanTy.getAsString(Context.PrintingPolicy); + if (CompareCanS == CanS) + continue; // No new info from canonical type + + ForceAKA = true; + break; + } // Check to see if we already desugared this type in this // diagnostic. If so, don't do it again. @@ -172,11 +201,15 @@ ConvertTypeToDiagnosticString(ASTContext &Context, QualType Ty, if (!Repeated) { bool ShouldAKA = false; QualType DesugaredTy = Desugar(Context, Ty, ShouldAKA); - if (ShouldAKA) { - S = "'" + S + "' (aka '"; - S += DesugaredTy.getAsString(Context.PrintingPolicy); - S += "')"; - return S; + if (ShouldAKA || ForceAKA) { + if (DesugaredTy == Ty) { + DesugaredTy = Ty.getCanonicalType(); + } + std::string akaStr = DesugaredTy.getAsString(Context.PrintingPolicy); + if (akaStr != S) { + S = "'" + S + "' (aka '" + akaStr + "')"; + return S; + } } } @@ -184,16 +217,18 @@ ConvertTypeToDiagnosticString(ASTContext &Context, QualType Ty, 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) { +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, + llvm::SmallVectorImpl<intptr_t> &QualTypeVals) { ASTContext &Context = *static_cast<ASTContext*>(Cookie); std::string S; @@ -206,7 +241,8 @@ void clang::FormatASTNodeDiagnosticArgument(Diagnostic::ArgumentKind Kind, "Invalid modifier for QualType argument"); QualType Ty(QualType::getFromOpaquePtr(reinterpret_cast<void*>(Val))); - S = ConvertTypeToDiagnosticString(Context, Ty, PrevArgs, NumPrevArgs); + S = ConvertTypeToDiagnosticString(Context, Ty, PrevArgs, NumPrevArgs, + QualTypeVals); NeedQuotes = false; break; } @@ -257,7 +293,7 @@ void clang::FormatASTNodeDiagnosticArgument(Diagnostic::ArgumentKind Kind, } else if (TypeDecl *Type = dyn_cast<TypeDecl>(DC)) { S = ConvertTypeToDiagnosticString(Context, Context.getTypeDeclType(Type), - PrevArgs, NumPrevArgs); + PrevArgs, NumPrevArgs, QualTypeVals); } else { // FIXME: Get these strings from some localized place NamedDecl *ND = cast<NamedDecl>(DC); diff --git a/lib/Basic/Diagnostic.cpp b/lib/Basic/Diagnostic.cpp index e0ef53d00d..ae363a0df0 100644 --- a/lib/Basic/Diagnostic.cpp +++ b/lib/Basic/Diagnostic.cpp @@ -26,7 +26,8 @@ static void DummyArgToStringFn(Diagnostic::ArgumentKind AK, intptr_t QT, const Diagnostic::ArgumentValue *PrevArgs, unsigned NumPrevArgs, llvm::SmallVectorImpl<char> &Output, - void *Cookie) { + void *Cookie, + llvm::SmallVectorImpl<intptr_t> &QualTypeVals) { const char *Str = "<can't format argument>"; Output.append(Str, Str+strlen(Str)); } @@ -544,7 +545,14 @@ FormatDiagnostic(const char *DiagStr, const char *DiagEnd, /// ConvertArgToString, allowing the implementation to avoid redundancies in /// obvious cases. llvm::SmallVector<Diagnostic::ArgumentValue, 8> FormattedArgs; - + + /// QualTypeVals - Pass a vector of arrays so that QualType names can be + /// compared to see if more information is needed to be printed. + llvm::SmallVector<intptr_t, 2> QualTypeVals; + for (unsigned i = 0, e = getNumArgs(); i < e; ++i) + if (getArgKind(i) == Diagnostic::ak_qualtype) + QualTypeVals.push_back(getRawArg(i)); + while (DiagStr != DiagEnd) { if (DiagStr[0] != '%') { // Append non-%0 substrings to Str if we have one. @@ -675,7 +683,7 @@ FormatDiagnostic(const char *DiagStr, const char *DiagEnd, Modifier, ModifierLen, Argument, ArgumentLen, FormattedArgs.data(), FormattedArgs.size(), - OutStr); + OutStr, QualTypeVals); break; } diff --git a/test/Misc/diag-aka-types.cpp b/test/Misc/diag-aka-types.cpp index e0e6b8c7c7..042c70b18a 100644 --- a/test/Misc/diag-aka-types.cpp +++ b/test/Misc/diag-aka-types.cpp @@ -12,3 +12,41 @@ char c2 = ref; // expected-error{{'const foo_t' (aka 'const X')}} // deduced auto should not produce an aka. auto aut = X(); char c3 = aut; // expected-error{{from 'X' to 'char'}} + +// There are two classes named Foo::foo here. Make sure the message gives +// a way to them apart. +namespace Foo { + class foo {}; +} + +namespace bar { + namespace Foo { + class foo; + } + void f(Foo::foo* x); // expected-note{{passing argument to parameter 'x' here}} +} + +void test(Foo::foo* x) { + bar::f(x); // expected-error{{cannot initialize a parameter of type 'Foo::foo *' (aka 'bar::Foo::foo *') with an lvalue of type 'Foo::foo *')}} +} + +// PR9548 - "no known conversion from 'vector<string>' to 'vector<string>'" +// vector<string> refers to two different types here. Make sure the message +// gives a way to tell them apart. +class versa_string; +typedef versa_string string; + +namespace std {template <typename T> class vector;} +using std::vector; + +void f(vector<string> v); // expected-note {{candidate function not viable: no known conversion from 'vector<string>' (aka 'std::vector<std::basic_string>') to 'vector<string>' (aka 'std::vector<versa_string>') for 1st argument}} + +namespace std { + class basic_string; + typedef basic_string string; + template <typename T> class vector {}; + void g() { + vector<string> v; + f(v); // expected-error{{no matching function for call to 'f'}} + } +} |