From: Chandler Carruth Date: Mon, 11 Jul 2011 17:49:21 +0000 (+0000) Subject: Apply patch from Richard Trieu to fix PR9548: X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0673cb30340aadaede7b795c763b00f6b64e611c;p=clang Apply patch from Richard Trieu to fix PR9548: When two different types has the same text representation in the same diagnostic message, print an a.k.a. after the type if the a.k.a. gives extra information about the type. class versa_string; typedef versa_string string; namespace std {template class vector;} using std::vector; void f(vector v); namespace std { class basic_string; typedef basic_string string; template class vector {}; void g() { vector v; f(v); } } Old message: ---------------- test.cc:15:3: error: no matching function for call to 'f' f(&v); ^ test.cc:7:6: note: candidate function not viable: no known conversion from 'vector' to 'vector' for 1st argument void f(vector v); ^ 1 error generated. New message: --------------- test.cc:15:3: error: no matching function for call to 'f' f(v); ^ test.cc:7:6: note: candidate function not viable: no known conversion from 'vector' (aka 'std::vector') to 'vector' (aka 'std::vector') for 1st argument void f(vector v); ^ 1 error generated. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@134904 91177308-0d34-0410-b5e6-96231b3b80d8 --- 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 &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 &Output, + void *Cookie, + llvm::SmallVectorImpl &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 &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 &Output, + void *Cookie, + llvm::SmallVectorImpl &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 &Output) const { + llvm::SmallVectorImpl &Output, + llvm::SmallVectorImpl &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 &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::iterator I = QualTypeVals.begin(), + E = QualTypeVals.end(); I != E; ++I) { + QualType CompareTy = + QualType::getFromOpaquePtr(reinterpret_cast(*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 &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 &Output, + void *Cookie, + llvm::SmallVectorImpl &QualTypeVals) { ASTContext &Context = *static_cast(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(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(DC)) { S = ConvertTypeToDiagnosticString(Context, Context.getTypeDeclType(Type), - PrevArgs, NumPrevArgs); + PrevArgs, NumPrevArgs, QualTypeVals); } else { // FIXME: Get these strings from some localized place NamedDecl *ND = cast(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 &Output, - void *Cookie) { + void *Cookie, + llvm::SmallVectorImpl &QualTypeVals) { const char *Str = ""; 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 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 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' to 'vector'" +// vector 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 class vector;} +using std::vector; + +void f(vector v); // expected-note {{candidate function not viable: no known conversion from 'vector' (aka 'std::vector') to 'vector' (aka 'std::vector') for 1st argument}} + +namespace std { + class basic_string; + typedef basic_string string; + template class vector {}; + void g() { + vector v; + f(v); // expected-error{{no matching function for call to 'f'}} + } +}