From: Richard Trieu Date: Tue, 26 Jun 2012 18:18:47 +0000 (+0000) Subject: Add template type diffing to Clang. This feature will provide a better X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=246b6aa6763de8c617d564ef33123a8f3293a80e;p=clang Add template type diffing to Clang. This feature will provide a better comparison between two templated types when they both appear in a diagnostic. Type elision will remove indentical template arguments, which can be disabled with -fno-elide-type. Cyan highlighting is applied to the differing types. For more formatting, -fdiagnostic-show-template-tree will output the template type as an indented text tree, with differences appearing inline. Template tree works with or without type elision. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@159216 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/Diagnostic.h b/include/clang/Basic/Diagnostic.h index 883e9d193e..4c3649372c 100644 --- a/include/clang/Basic/Diagnostic.h +++ b/include/clang/Basic/Diagnostic.h @@ -153,7 +153,8 @@ public: ak_declarationname, // DeclarationName ak_nameddecl, // NamedDecl * ak_nestednamespec, // NestedNameSpecifier * - ak_declcontext // DeclContext * + ak_declcontext, // DeclContext * + ak_qualtype_pair // pair }; /// Specifies which overload candidates to display when overload resolution @@ -175,6 +176,9 @@ private: bool ErrorsAsFatal; // Treat errors like fatal errors. bool SuppressSystemWarnings; // Suppress warnings in system headers. bool SuppressAllDiagnostics; // Suppress all diagnostics. + bool ElideType; // Elide common types of templates. + bool PrintTemplateTree; // Print a tree when comparing templates. + bool ShowColors; // Color printing is enabled. OverloadsShown ShowOverloads; // Which overload candidates to show. unsigned ErrorLimit; // Cap of # errors emitted, 0 -> no limit. unsigned TemplateBacktraceLimit; // Cap on depth of template backtrace stack, @@ -438,7 +442,22 @@ public: SuppressAllDiagnostics = Val; } bool getSuppressAllDiagnostics() const { return SuppressAllDiagnostics; } - + + /// \brief Set type eliding, to skip outputting same types occurring in + /// template types. + void setElideType(bool Val = true) { ElideType = Val; } + bool getElideType() { return ElideType; } + + /// \brief Set tree printing, to outputting the template difference in a + /// tree format. + void setPrintTemplateTree(bool Val = false) { PrintTemplateTree = Val; } + bool getPrintTemplateTree() { return PrintTemplateTree; } + + /// \brief Set color printing, so the type diffing will inject color markers + /// into the output. + void setShowColors(bool Val = false) { ShowColors = Val; } + bool getShowColors() { return ShowColors; } + /// \brief Specify which overload candidates to show when overload resolution /// fails. By default, we show all candidates. void setShowOverloads(OverloadsShown Val) { @@ -1058,7 +1077,6 @@ public: return DiagObj->DiagArgumentsVal[Idx]; } - /// getNumRanges - Return the number of source ranges associated with this /// diagnostic. unsigned getNumRanges() const { @@ -1223,6 +1241,20 @@ class IgnoringDiagConsumer : public DiagnosticConsumer { } }; +// Struct used for sending info about how a type should be printed. +struct TemplateDiffTypes { + intptr_t FromType; + intptr_t ToType; + unsigned PrintTree : 1; + unsigned PrintFromType : 1; + unsigned ElideType : 1; + unsigned ShowColors : 1; +}; + +/// Special character that the diagnostic printer will use to toggle the bold +/// attribute. The character itself will be not be printed. +const char ToggleHighlight = 127; + } // end namespace clang #endif diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 41b8b8a913..38a22a7fce 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2162,7 +2162,7 @@ def note_ovl_candidate_bad_conv : Note<"candidate " "function (the implicit copy assignment operator)|" "function (the implicit move assignment operator)|" "constructor (inherited)}0%1" - " not viable: no known conversion from %2 to %3 for " + " not viable: no known conversion %diff{from | to | }2,3for " "%select{%ordinal5 argument|object argument}4; " "%select{|dereference the argument with *|" "take the address of the argument with &|" diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index 16a4366ce8..0161e2d48b 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -358,6 +358,9 @@ def fdiagnostics_show_note_include_stack : Flag<"-fdiagnostics-show-note-include Group, Flags<[CC1Option]>, HelpText<"Display include stacks for diagnostic notes">; def fdiagnostics_format_EQ : Joined<"-fdiagnostics-format=">, Group; def fdiagnostics_show_category_EQ : Joined<"-fdiagnostics-show-category=">, Group; +def fdiagnostics_show_template_tree : Flag<"-fdiagnostics-show-template-tree">, + Group, Flags<[CC1Option]>, + HelpText<"Print a template comparison tree for differing templates">; def fdollars_in_identifiers : Flag<"-fdollars-in-identifiers">, Group, HelpText<"Allow '$' in identifiers">, Flags<[CC1Option]>; def fdwarf2_cfi_asm : Flag<"-fdwarf2-cfi-asm">, Group; @@ -365,6 +368,9 @@ def fno_dwarf2_cfi_asm : Flag<"-fno-dwarf2-cfi-asm">, Group, Flags<[CC def fdwarf_directory_asm : Flag<"-fdwarf-directory-asm">, Group; def fno_dwarf_directory_asm : Flag<"-fno-dwarf-directory-asm">, Group, Flags<[CC1Option]>; def felide_constructors : Flag<"-felide-constructors">, Group; +def fno_elide_type : Flag<"-fno-elide-type">, Group, + Flags<[CC1Option]>, + HelpText<"Do not elide types when printing diagnostics">; def feliminate_unused_debug_symbols : Flag<"-feliminate-unused-debug-symbols">, Group; def femit_all_decls : Flag<"-femit-all-decls">, Group, Flags<[CC1Option]>, HelpText<"Emit all declarations, even if unused">; diff --git a/include/clang/Frontend/DiagnosticOptions.h b/include/clang/Frontend/DiagnosticOptions.h index 1c6ba6acc3..8dec37ca03 100644 --- a/include/clang/Frontend/DiagnosticOptions.h +++ b/include/clang/Frontend/DiagnosticOptions.h @@ -47,6 +47,9 @@ public: /// diagnostics, indicated by markers in the /// input source file. + unsigned ElideType: 1; /// Elide identical types in template diffing + unsigned ShowTemplateTree: 1; /// Print a template tree when diffing + unsigned ErrorLimit; /// Limit # errors emitted. unsigned MacroBacktraceLimit; /// Limit depth of macro expansion backtrace. unsigned TemplateBacktraceLimit; /// Limit depth of instantiation backtrace. diff --git a/lib/AST/ASTDiagnostic.cpp b/lib/AST/ASTDiagnostic.cpp index ca4fe26852..a5733237ef 100644 --- a/lib/AST/ASTDiagnostic.cpp +++ b/lib/AST/ASTDiagnostic.cpp @@ -14,7 +14,11 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/TemplateBase.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/Type.h" +#include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -225,6 +229,11 @@ ConvertTypeToDiagnosticString(ASTContext &Context, QualType Ty, return S; } +static bool FormatTemplateTypeDiff(ASTContext &Context, QualType FromType, + QualType ToType, bool PrintTree, + bool PrintFromType, bool ElideType, + bool ShowColors, std::string &S); + void clang::FormatASTNodeDiagnosticArgument( DiagnosticsEngine::ArgumentKind Kind, intptr_t Val, @@ -244,6 +253,32 @@ void clang::FormatASTNodeDiagnosticArgument( switch (Kind) { default: llvm_unreachable("unknown ArgumentKind"); + case DiagnosticsEngine::ak_qualtype_pair: { + const TemplateDiffTypes &TDT = *reinterpret_cast(Val); + QualType FromType = + QualType::getFromOpaquePtr(reinterpret_cast(TDT.FromType)); + QualType ToType = + QualType::getFromOpaquePtr(reinterpret_cast(TDT.ToType)); + + if (FormatTemplateTypeDiff(Context, FromType, ToType, TDT.PrintTree, + TDT.PrintFromType, TDT.ElideType, + TDT.ShowColors, S)) { + NeedQuotes = !TDT.PrintTree; + break; + } + + // Don't fall-back during tree printing. The caller will handle + // this case. + if (TDT.PrintTree) + return; + + // Attempting to do a templete diff on non-templates. Set the variables + // and continue with regular type printing of the appropriate type. + Val = TDT.PrintFromType ? TDT.FromType : TDT.ToType; + ModLen = 0; + ArgLen = 0; + // Fall through + } case DiagnosticsEngine::ak_qualtype: { assert(ModLen == 0 && ArgLen == 0 && "Invalid modifier for QualType argument"); @@ -329,3 +364,901 @@ void clang::FormatASTNodeDiagnosticArgument( if (NeedQuotes) Output.push_back('\''); } + +/// TemplateDiff - A class that constructs a pretty string for a pair of +/// QualTypes. For the pair of types, a diff tree will be created containing +/// all the information about the templates and template arguments. Afterwards, +/// the tree is transformed to a string according to the options passed in. +namespace { +class TemplateDiff { + /// Context - The ASTContext which is used for comparing template arguments. + ASTContext &Context; + + /// Policy - Used during expression printing. + PrintingPolicy Policy; + + /// ElideType - Option to elide identical types. + bool ElideType; + + /// PrintTree - Format output string as a tree. + bool PrintTree; + + /// ShowColor - Diagnostics support color, so bolding will be used. + bool ShowColor; + + /// FromType - When single type printing is selected, this is the type to be + /// be printed. When tree printing is selected, this type will show up first + /// in the tree. + QualType FromType; + + /// ToType - The type that FromType is compared to. Only in tree printing + /// will this type be outputed. + QualType ToType; + + /// Str - Storage for the output stream. + llvm::SmallString<128> Str; + + /// OS - The stream used to construct the output strings. + llvm::raw_svector_ostream OS; + + /// IsBold - Keeps track of the bold formatting for the output string. + bool IsBold; + + /// DiffTree - A tree representation the differences between two types. + class DiffTree { + /// DiffNode - The root node stores the original type. Each child node + /// stores template arguments of their parents. For templated types, the + /// template decl is also stored. + struct DiffNode { + /// NextNode - The index of the next sibling node or 0. + unsigned NextNode; + + /// ChildNode - The index of the first child node or 0. + unsigned ChildNode; + + /// ParentNode - The index of the parent node. + unsigned ParentNode; + + /// FromType, ToType - The type arguments. + QualType FromType, ToType; + + /// FromExpr, ToExpr - The expression arguments. + Expr *FromExpr, *ToExpr; + + /// FromTD, ToTD - The template decl for template template + /// arguments or the type arguments that are templates. + TemplateDecl *FromTD, *ToTD; + + /// FromDefault, ToDefault - Whether the argument is a default argument. + bool FromDefault, ToDefault; + + /// Same - Whether the two arguments evaluate to the same value. + bool Same; + + DiffNode(unsigned ParentNode = 0) + : NextNode(0), ChildNode(0), ParentNode(ParentNode), + FromType(), ToType(), FromExpr(0), ToExpr(0), FromTD(0), ToTD(0), + FromDefault(false), ToDefault(false), Same(false) { } + }; + + /// FlatTree - A flattened tree used to store the DiffNodes. + llvm::SmallVector FlatTree; + + /// CurrentNode - The index of the current node being used. + unsigned CurrentNode; + + /// NextFreeNode - The index of the next unused node. Used when creating + /// child nodes. + unsigned NextFreeNode; + + /// ReadNode - The index of the current node being read. + unsigned ReadNode; + + public: + DiffTree() : + CurrentNode(0), NextFreeNode(1) { + FlatTree.push_back(DiffNode()); + } + + // Node writing functions. + /// SetNode - Sets FromTD and ToTD of the current node. + void SetNode(TemplateDecl *FromTD, TemplateDecl *ToTD) { + FlatTree[CurrentNode].FromTD = FromTD; + FlatTree[CurrentNode].ToTD = ToTD; + } + + /// SetNode - Sets FromType and ToType of the current node. + void SetNode(QualType FromType, QualType ToType) { + FlatTree[CurrentNode].FromType = FromType; + FlatTree[CurrentNode].ToType = ToType; + } + + /// SetNode - Set FromExpr and ToExpr of the current node. + void SetNode(Expr *FromExpr, Expr *ToExpr) { + FlatTree[CurrentNode].FromExpr = FromExpr; + FlatTree[CurrentNode].ToExpr = ToExpr; + } + + /// SetSame - Sets the same flag of the current node. + void SetSame(bool Same) { + FlatTree[CurrentNode].Same = Same; + } + + /// SetDefault - Sets FromDefault and ToDefault flags of the current node. + void SetDefault(bool FromDefault, bool ToDefault) { + FlatTree[CurrentNode].FromDefault = FromDefault; + FlatTree[CurrentNode].ToDefault = ToDefault; + } + + /// Up - Changes the node to the parent of the current node. + void Up() { + CurrentNode = FlatTree[CurrentNode].ParentNode; + } + + /// AddNode - Adds a child node to the current node, then sets that node + /// node as the current node. + void AddNode() { + FlatTree.push_back(DiffNode(CurrentNode)); + DiffNode &Node = FlatTree[CurrentNode]; + if (Node.ChildNode == 0) { + // If a child node doesn't exist, add one. + Node.ChildNode = NextFreeNode; + } else { + // If a child node exists, find the last child node and add a + // next node to it. + unsigned i; + for (i = Node.ChildNode; FlatTree[i].NextNode != 0; + i = FlatTree[i].NextNode) { + } + FlatTree[i].NextNode = NextFreeNode; + } + CurrentNode = NextFreeNode; + ++NextFreeNode; + } + + // Node reading functions. + /// StartTraverse - Prepares the tree for recursive traversal. + void StartTraverse() { + ReadNode = 0; + CurrentNode = NextFreeNode; + NextFreeNode = 0; + } + + /// Parent - Move the current read node to its parent. + void Parent() { + ReadNode = FlatTree[ReadNode].ParentNode; + } + + /// NodeIsTemplate - Returns true if a template decl is set, and types are + /// set. + bool NodeIsTemplate() { + return (FlatTree[ReadNode].FromTD && + !FlatTree[ReadNode].ToType.isNull()) || + (FlatTree[ReadNode].ToTD && !FlatTree[ReadNode].ToType.isNull()); + } + + /// NodeIsQualType - Returns true if a Qualtype is set. + bool NodeIsQualType() { + return !FlatTree[ReadNode].FromType.isNull() || + !FlatTree[ReadNode].ToType.isNull(); + } + + /// NodeIsExpr - Returns true if an expr is set. + bool NodeIsExpr() { + return FlatTree[ReadNode].FromExpr || FlatTree[ReadNode].ToExpr; + } + + /// NodeIsTemplateTemplate - Returns true if the argument is a template + /// template type. + bool NodeIsTemplateTemplate() { + return FlatTree[ReadNode].FromType.isNull() && + FlatTree[ReadNode].ToType.isNull() && + (FlatTree[ReadNode].FromTD || FlatTree[ReadNode].ToTD); + } + + /// GetNode - Gets the FromType and ToType. + void GetNode(QualType &FromType, QualType &ToType) { + FromType = FlatTree[ReadNode].FromType; + ToType = FlatTree[ReadNode].ToType; + } + + /// GetNode - Gets the FromExpr and ToExpr. + void GetNode(Expr *&FromExpr, Expr *&ToExpr) { + FromExpr = FlatTree[ReadNode].FromExpr; + ToExpr = FlatTree[ReadNode].ToExpr; + } + + /// GetNode - Gets the FromTD and ToTD. + void GetNode(TemplateDecl *&FromTD, TemplateDecl *&ToTD) { + FromTD = FlatTree[ReadNode].FromTD; + ToTD = FlatTree[ReadNode].ToTD; + } + + /// NodeIsSame - Returns true the arguments are the same. + bool NodeIsSame() { + return FlatTree[ReadNode].Same; + } + + /// HasChildrend - Returns true if the node has children. + bool HasChildren() { + return FlatTree[ReadNode].ChildNode != 0; + } + + /// MoveToChild - Moves from the current node to its child. + void MoveToChild() { + ReadNode = FlatTree[ReadNode].ChildNode; + } + + /// AdvanceSibling - If there is a next sibling, advance to it and return + /// true. Otherwise, return false. + bool AdvanceSibling() { + if (FlatTree[ReadNode].NextNode == 0) + return false; + + ReadNode = FlatTree[ReadNode].NextNode; + return true; + } + + /// HasNextSibling - Return true if the node has a next sibling. + bool HasNextSibling() { + return FlatTree[ReadNode].NextNode != 0; + } + + /// FromDefault - Return true if the from argument is the default. + bool FromDefault() { + return FlatTree[ReadNode].FromDefault; + } + + /// ToDefault - Return true if the to argument is the default. + bool ToDefault() { + return FlatTree[ReadNode].ToDefault; + } + + /// Empty - Returns true if the tree has no information. + bool Empty() { + return !FlatTree[0].FromTD && !FlatTree[0].ToTD && + !FlatTree[0].FromExpr && !FlatTree[0].ToExpr && + FlatTree[0].FromType.isNull() && FlatTree[0].ToType.isNull(); + } + }; + + DiffTree Tree; + + /// TSTiterator - an iterator that is used to enter a + /// TemplateSpecializationType and read TemplateArguments inside template + /// parameter packs in order with the rest of the TemplateArguments. + struct TSTiterator { + typedef const TemplateArgument& reference; + typedef const TemplateArgument* pointer; + + /// TST - the template specialization whose arguments this iterator + /// traverse over. + const TemplateSpecializationType *TST; + + /// Index - the index of the template argument in TST. + unsigned Index; + + /// CurrentTA - if CurrentTA is not the same as EndTA, then CurrentTA + /// points to a TemplateArgument within a parameter pack. + TemplateArgument::pack_iterator CurrentTA; + + /// EndTA - the end iterator of a parameter pack + TemplateArgument::pack_iterator EndTA; + + /// TSTiterator - Constructs an iterator and sets it to the first template + /// argument. + TSTiterator(const TemplateSpecializationType *TST) + : TST(TST), Index(0), CurrentTA(0), EndTA(0) { + if (isEnd()) return; + + // Set to first template argument. If not a parameter pack, done. + TemplateArgument TA = TST->getArg(0); + if (TA.getKind() != TemplateArgument::Pack) return; + + // Start looking into the parameter pack. + CurrentTA = TA.pack_begin(); + EndTA = TA.pack_end(); + + // Found a valid template argument. + if (CurrentTA != EndTA) return; + + // Parameter pack is empty, use the increment to get to a valid + // template argument. + ++(*this); + } + + /// isEnd - Returns true if the iterator is one past the end. + bool isEnd() const { + return Index == TST->getNumArgs(); + } + + /// &operator++ - Increment the iterator to the next template argument. + TSTiterator &operator++() { + assert(!isEnd() && "Iterator incremented past end of arguments."); + + // If in a parameter pack, advance in the parameter pack. + if (CurrentTA != EndTA) { + ++CurrentTA; + if (CurrentTA != EndTA) + return *this; + } + + // Loop until a template argument is found, or the end is reached. + while (true) { + // Advance to the next template argument. Break if reached the end. + if (++Index == TST->getNumArgs()) break; + + // If the TemplateArgument is not a parameter pack, done. + TemplateArgument TA = TST->getArg(Index); + if (TA.getKind() != TemplateArgument::Pack) break; + + // Handle parameter packs. + CurrentTA = TA.pack_begin(); + EndTA = TA.pack_end(); + + // If the parameter pack is empty, try to advance again. + if (CurrentTA != EndTA) break; + } + return *this; + } + + /// operator* - Returns the appropriate TemplateArgument. + reference operator*() const { + assert(!isEnd() && "Index exceeds number of arguments."); + if (CurrentTA == EndTA) + return TST->getArg(Index); + else + return *CurrentTA; + } + + /// operator-> - Allow access to the underlying TemplateArgument. + pointer operator->() const { + return &operator*(); + } + }; + + // These functions build up the template diff tree, including functions to + // retrieve and compare template arguments. + + static const TemplateSpecializationType * GetTemplateSpecializationType( + ASTContext &Context, QualType Ty) { + if (const TemplateSpecializationType *TST = + Ty->getAs()) + return TST; + + const RecordType *RT = Ty->getAs(); + + if (!RT) + return 0; + + const ClassTemplateSpecializationDecl *CTSD = + dyn_cast(RT->getDecl()); + + if (!CTSD) + return 0; + + Ty = Context.getTemplateSpecializationType( + TemplateName(CTSD->getSpecializedTemplate()), + CTSD->getTemplateArgs().data(), + CTSD->getTemplateArgs().size(), + Ty.getCanonicalType()); + + return Ty->getAs(); + } + + /// DiffTemplate - recursively visits template arguments and stores the + /// argument info into a tree. + void DiffTemplate(const TemplateSpecializationType *FromTST, + const TemplateSpecializationType *ToTST) { + // Begin descent into diffing template tree. + TemplateParameterList *Params = + FromTST->getTemplateName().getAsTemplateDecl()->getTemplateParameters(); + unsigned TotalArgs = 0; + for (TSTiterator FromIter(FromTST), ToIter(ToTST); + !FromIter.isEnd() || !ToIter.isEnd(); ++TotalArgs) { + Tree.AddNode(); + + // Get the parameter at index TotalArgs. If index is larger + // than the total number of parameters, then there is an + // argument pack, so re-use the last parameter. + NamedDecl *ParamND = Params->getParam( + (TotalArgs < Params->size()) ? TotalArgs + : Params->size() - 1); + // Handle Types + if (TemplateTypeParmDecl *DefaultTTPD = + dyn_cast(ParamND)) { + QualType FromType, ToType; + GetType(FromIter, DefaultTTPD, FromType); + GetType(ToIter, DefaultTTPD, ToType); + Tree.SetNode(FromType, ToType); + Tree.SetDefault(FromIter.isEnd() && !FromType.isNull(), + ToIter.isEnd() && !ToType.isNull()); + if (!FromType.isNull() && !ToType.isNull()) { + if (Context.hasSameType(FromType, ToType)) { + Tree.SetSame(true); + } else { + const TemplateSpecializationType *FromArgTST = + GetTemplateSpecializationType(Context, FromType); + const TemplateSpecializationType *ToArgTST = + GetTemplateSpecializationType(Context, ToType); + + if (FromArgTST && ToArgTST) { + bool SameTemplate = hasSameTemplate(FromArgTST, ToArgTST); + if (SameTemplate) { + Tree.SetNode(FromArgTST->getTemplateName().getAsTemplateDecl(), + ToArgTST->getTemplateName().getAsTemplateDecl()); + DiffTemplate(FromArgTST, ToArgTST); + } + } + } + } + } + + // Handle Expressions + if (NonTypeTemplateParmDecl *DefaultNTTPD = + dyn_cast(ParamND)) { + Expr *FromExpr, *ToExpr; + GetExpr(FromIter, DefaultNTTPD, FromExpr); + GetExpr(ToIter, DefaultNTTPD, ToExpr); + Tree.SetNode(FromExpr, ToExpr); + Tree.SetSame(IsEqualExpr(Context, FromExpr, ToExpr)); + Tree.SetDefault(FromIter.isEnd() && FromExpr, + ToIter.isEnd() && ToExpr); + } + + // Handle Templates + if (TemplateTemplateParmDecl *DefaultTTPD = + dyn_cast(ParamND)) { + TemplateDecl *FromDecl, *ToDecl; + GetTemplateDecl(FromIter, DefaultTTPD, FromDecl); + GetTemplateDecl(ToIter, DefaultTTPD, ToDecl); + Tree.SetNode(FromDecl, ToDecl); + Tree.SetSame(FromDecl && ToDecl && + FromDecl->getIdentifier() == ToDecl->getIdentifier()); + } + + if (!FromIter.isEnd()) ++FromIter; + if (!ToIter.isEnd()) ++ToIter; + Tree.Up(); + } + } + + /// hasSameTemplate - Returns true if both types are specialized from the + /// same template declaration. If they come from different template aliases, + /// do a parallel ascension search to determine the highest template alias in + /// common and set the arguments to them. + static bool hasSameTemplate(const TemplateSpecializationType *&FromTST, + const TemplateSpecializationType *&ToTST) { + // Check the top templates if they are the same. + if (FromTST->getTemplateName().getAsTemplateDecl()->getIdentifier() == + ToTST->getTemplateName().getAsTemplateDecl()->getIdentifier()) + return true; + + // Create vectors of template aliases. + SmallVector FromTemplateList, + ToTemplateList; + + const TemplateSpecializationType *TempToTST = ToTST, *TempFromTST = FromTST; + FromTemplateList.push_back(FromTST); + ToTemplateList.push_back(ToTST); + + // Dump every template alias into the vectors. + while (TempFromTST->isTypeAlias()) { + TempFromTST = + TempFromTST->getAliasedType()->getAs(); + if (!TempFromTST) + break; + FromTemplateList.push_back(TempFromTST); + } + while (TempToTST->isTypeAlias()) { + TempToTST = + TempToTST->getAliasedType()->getAs(); + if (!TempToTST) + break; + ToTemplateList.push_back(TempToTST); + } + + SmallVector::reverse_iterator + FromIter = FromTemplateList.rbegin(), FromEnd = FromTemplateList.rend(), + ToIter = ToTemplateList.rbegin(), ToEnd = ToTemplateList.rend(); + + // Check if the lowest template types are the same. If not, return. + if ((*FromIter)->getTemplateName().getAsTemplateDecl()->getIdentifier() != + (*ToIter)->getTemplateName().getAsTemplateDecl()->getIdentifier()) + return false; + + // Begin searching up the template aliases. The bottom most template + // matches so move up until one pair does not match. Use the template + // right before that one. + for (; FromIter != FromEnd && ToIter != ToEnd; ++FromIter, ++ToIter) { + if ((*FromIter)->getTemplateName().getAsTemplateDecl()->getIdentifier() != + (*ToIter)->getTemplateName().getAsTemplateDecl()->getIdentifier()) + break; + } + + FromTST = FromIter[-1]; + ToTST = ToIter[-1]; + + return true; + } + + /// GetType - Retrieves the template type arguments, including default + /// arguments. + void GetType(const TSTiterator &Iter, TemplateTypeParmDecl *DefaultTTPD, + QualType &ArgType) { + ArgType = QualType(); + bool isVariadic = DefaultTTPD->isParameterPack(); + + if (!Iter.isEnd()) + ArgType = Iter->getAsType(); + else if (!isVariadic) + ArgType = DefaultTTPD->getDefaultArgument(); + }; + + /// GetExpr - Retrieves the template expression argument, including default + /// arguments. + void GetExpr(const TSTiterator &Iter, NonTypeTemplateParmDecl *DefaultNTTPD, + Expr *&ArgExpr) { + ArgExpr = 0; + bool isVariadic = DefaultNTTPD->isParameterPack(); + + if (!Iter.isEnd()) + ArgExpr = Iter->getAsExpr(); + else if (!isVariadic) + ArgExpr = DefaultNTTPD->getDefaultArgument(); + + if (ArgExpr) + while (SubstNonTypeTemplateParmExpr *SNTTPE = + dyn_cast(ArgExpr)) + ArgExpr = SNTTPE->getReplacement(); + } + + /// GetTemplateDecl - Retrieves the template template arguments, including + /// default arguments. + void GetTemplateDecl(const TSTiterator &Iter, + TemplateTemplateParmDecl *DefaultTTPD, + TemplateDecl *&ArgDecl) { + ArgDecl = 0; + bool isVariadic = DefaultTTPD->isParameterPack(); + + TemplateArgument TA = DefaultTTPD->getDefaultArgument().getArgument(); + TemplateDecl *DefaultTD = TA.getAsTemplate().getAsTemplateDecl(); + + if (!Iter.isEnd()) + ArgDecl = Iter->getAsTemplate().getAsTemplateDecl(); + else if (!isVariadic) + ArgDecl = DefaultTD; + } + + /// IsEqualExpr - Returns true if the expressions evaluate to the same value. + static bool IsEqualExpr(ASTContext &Context, Expr *FromExpr, Expr *ToExpr) { + if (FromExpr == ToExpr) + return true; + + if (!FromExpr || !ToExpr) + return false; + + FromExpr = FromExpr->IgnoreParens(); + ToExpr = ToExpr->IgnoreParens(); + + DeclRefExpr *FromDRE = dyn_cast(FromExpr), + *ToDRE = dyn_cast(ToExpr); + + if (FromDRE || ToDRE) { + if (!FromDRE || !ToDRE) + return false; + return FromDRE->getDecl() == ToDRE->getDecl(); + } + + Expr::EvalResult FromResult, ToResult; + if (!FromExpr->EvaluateAsRValue(FromResult, Context) || + !ToExpr->EvaluateAsRValue(ToResult, Context)) + assert(0 && "Template arguments must be known at compile time."); + + APValue &FromVal = FromResult.Val; + APValue &ToVal = ToResult.Val; + + if (FromVal.getKind() != ToVal.getKind()) return false; + + switch (FromVal.getKind()) { + case APValue::Int: + return FromVal.getInt() == ToVal.getInt(); + case APValue::LValue: { + APValue::LValueBase FromBase = FromVal.getLValueBase(); + APValue::LValueBase ToBase = ToVal.getLValueBase(); + if (FromBase.isNull() && ToBase.isNull()) + return true; + if (FromBase.isNull() || ToBase.isNull()) + return false; + return FromBase.get() == + ToBase.get(); + } + case APValue::MemberPointer: + return FromVal.getMemberPointerDecl() == ToVal.getMemberPointerDecl(); + default: + llvm_unreachable("Unknown template argument expression."); + } + } + + // These functions converts the tree representation of the template + // differences into the internal character vector. + + /// TreeToString - Converts the Tree object into a character stream which + /// will later be turned into the output string. + void TreeToString(int Indent = 1) { + if (PrintTree) { + OS << '\n'; + for (int i = 0; i < Indent; ++i) + OS << " "; + ++Indent; + } + + // Handle cases where the difference is not templates with different + // arguments. + if (!Tree.NodeIsTemplate()) { + if (Tree.NodeIsQualType()) { + QualType FromType, ToType; + Tree.GetNode(FromType, ToType); + PrintTypeNames(FromType, ToType, Tree.FromDefault(), Tree.ToDefault(), + Tree.NodeIsSame()); + return; + } + if (Tree.NodeIsExpr()) { + Expr *FromExpr, *ToExpr; + Tree.GetNode(FromExpr, ToExpr); + PrintExpr(FromExpr, ToExpr, Tree.FromDefault(), Tree.ToDefault(), + Tree.NodeIsSame()); + return; + } + if (Tree.NodeIsTemplateTemplate()) { + TemplateDecl *FromTD, *ToTD; + Tree.GetNode(FromTD, ToTD); + PrintTemplateTemplate(FromTD, ToTD, Tree.FromDefault(), + Tree.ToDefault(), Tree.NodeIsSame()); + return; + } + llvm_unreachable("Unable to deduce template difference."); + } + + // Node is root of template. Recurse on children. + TemplateDecl *FromTD, *ToTD; + Tree.GetNode(FromTD, ToTD); + + assert(Tree.HasChildren() && "Template difference not found in diff tree."); + + OS << FromTD->getNameAsString() << '<'; + Tree.MoveToChild(); + unsigned NumElideArgs = 0; + do { + if (ElideType) { + if (Tree.NodeIsSame()) { + ++NumElideArgs; + continue; + } + if (NumElideArgs > 0) { + PrintElideArgs(NumElideArgs, Indent); + NumElideArgs = 0; + OS << ", "; + } + } + TreeToString(Indent); + if (Tree.HasNextSibling()) + OS << ", "; + } while (Tree.AdvanceSibling()); + if (NumElideArgs > 0) + PrintElideArgs(NumElideArgs, Indent); + + Tree.Parent(); + OS << ">"; + } + + // To signal to the text printer that a certain text needs to be bolded, + // a special character is injected into the character stream which the + // text printer will later strip out. + + /// Bold - Start bolding text. + void Bold() { + assert(!IsBold && "Attempting to bold text that is already bold."); + IsBold = true; + if (ShowColor) + OS << ToggleHighlight; + } + + /// Unbold - Stop bolding text. + void Unbold() { + assert(IsBold && "Attempting to remove bold from unbold text."); + IsBold = false; + if (ShowColor) + OS << ToggleHighlight; + } + + // Functions to print out the arguments and highlighting the difference. + + /// PrintTypeNames - prints the typenames, bolding differences. Will detect + /// typenames that are the same and attempt to disambiguate them by using + /// canonical typenames. + void PrintTypeNames(QualType FromType, QualType ToType, + bool FromDefault, bool ToDefault, bool Same) { + assert((!FromType.isNull() || !ToType.isNull()) && + "Only one template argument may be missing."); + + if (Same) { + OS << FromType.getAsString(); + return; + } + + std::string FromTypeStr = FromType.isNull() ? "(no argument)" + : FromType.getAsString(); + std::string ToTypeStr = ToType.isNull() ? "(no argument)" + : ToType.getAsString(); + // Switch to canonical typename if it is better. + // TODO: merge this with other aka printing above. + if (FromTypeStr == ToTypeStr) { + std::string FromCanTypeStr = FromType.getCanonicalType().getAsString(); + std::string ToCanTypeStr = ToType.getCanonicalType().getAsString(); + if (FromCanTypeStr != ToCanTypeStr) { + FromTypeStr = FromCanTypeStr; + ToTypeStr = ToCanTypeStr; + } + } + + if (PrintTree) OS << '['; + OS << (FromDefault ? "(default) " : ""); + Bold(); + OS << FromTypeStr; + Unbold(); + if (PrintTree) { + OS << " != " << (ToDefault ? "(default) " : ""); + Bold(); + OS << ToTypeStr; + Unbold(); + OS << "]"; + } + return; + } + + /// PrintExpr - Prints out the expr template arguments, highlighting argument + /// differences. + void PrintExpr(const Expr *FromExpr, const Expr *ToExpr, + bool FromDefault, bool ToDefault, bool Same) { + assert((FromExpr || ToExpr) && + "Only one template argument may be missing."); + if (Same) { + PrintExpr(FromExpr); + } else if (!PrintTree) { + OS << (FromDefault ? "(default) " : ""); + Bold(); + PrintExpr(FromExpr); + Unbold(); + } else { + OS << (FromDefault ? "[(default) " : "["); + Bold(); + PrintExpr(FromExpr); + Unbold(); + OS << " != " << (ToDefault ? "(default) " : ""); + Bold(); + PrintExpr(ToExpr); + Unbold(); + OS << ']'; + } + } + + /// PrintExpr - Actual formatting and printing of expressions. + void PrintExpr(const Expr *E) { + if (!E) + OS << "(no argument)"; + else + E->printPretty(OS, Context, 0, Policy); return; + } + + /// PrintTemplateTemplate - Handles printing of template template arguments, + /// highlighting argument differences. + void PrintTemplateTemplate(TemplateDecl *FromTD, TemplateDecl *ToTD, + bool FromDefault, bool ToDefault, bool Same) { + assert((FromTD || ToTD) && "Only one template argument may be missing."); + if (Same) { + OS << "template " << FromTD->getNameAsString(); + } else if (!PrintTree) { + OS << (FromDefault ? "(default) template " : "template "); + Bold(); + OS << (FromTD ? FromTD->getNameAsString() : "(no argument)"); + Unbold(); + } else { + OS << (FromDefault ? "[(default) template " : "[template "); + Bold(); + OS << (FromTD ? FromTD->getNameAsString() : "(no argument)"); + Unbold(); + OS << " != " << (ToDefault ? "(default) template " : "template "); + Bold(); + OS << (ToTD ? ToTD->getNameAsString() : "(no argument)"); + Unbold(); + OS << ']'; + } + } + + // Prints the appropriate placeholder for elided template arguments. + void PrintElideArgs(unsigned NumElideArgs, unsigned Indent) { + if (PrintTree) { + OS << '\n'; + for (unsigned i = 0; i < Indent; ++i) + OS << " "; + } + if (NumElideArgs == 0) return; + if (NumElideArgs == 1) + OS << "[...]"; + else + OS << "[" << NumElideArgs << " * ...]"; + } + +public: + + TemplateDiff(ASTContext &Context, QualType FromType, QualType ToType, + bool PrintTree, bool PrintFromType, bool ElideType, + bool ShowColor) + : Context(Context), + Policy(Context.getLangOpts()), + ElideType(ElideType), + PrintTree(PrintTree), + ShowColor(ShowColor), + // When printing a single type, the FromType is the one printed. + FromType(PrintFromType ? FromType : ToType), + ToType(PrintFromType ? ToType : FromType), + OS(Str), + IsBold(false) { + } + + /// DiffTemplate - Start the template type diffing. + void DiffTemplate() { + const TemplateSpecializationType *FromOrigTST = + GetTemplateSpecializationType(Context, FromType); + const TemplateSpecializationType *ToOrigTST = + GetTemplateSpecializationType(Context, ToType); + + // Only checking templates. + if (!FromOrigTST || !ToOrigTST) + return; + + // Different base templates. + if (!hasSameTemplate(FromOrigTST, ToOrigTST)) { + return; + } + + Tree.SetNode(FromType, ToType); + + // Same base template, but different arguments. + Tree.SetNode(FromOrigTST->getTemplateName().getAsTemplateDecl(), + ToOrigTST->getTemplateName().getAsTemplateDecl()); + + DiffTemplate(FromOrigTST, ToOrigTST); + }; + + /// MakeString - When the two types given are templated types with the same + /// base template, a string representation of the type difference will be + /// loaded into S and return true. Otherwise, return false. + bool MakeString(std::string &S) { + Tree.StartTraverse(); + if (Tree.Empty()) + return false; + + TreeToString(); + assert(!IsBold && "Bold is applied to end of string."); + S = OS.str(); + return true; + } +}; // end class TemplateDiff +} // end namespace + +/// FormatTemplateTypeDiff - A helper static function to start the template +/// diff and return the properly formatted string. Returns true if the diff +/// is successful. +static bool FormatTemplateTypeDiff(ASTContext &Context, QualType FromType, + QualType ToType, bool PrintTree, + bool PrintFromType, bool ElideType, + bool ShowColors, std::string &S) { + if (PrintTree) + PrintFromType = true; + TemplateDiff TD(Context, FromType, ToType, PrintTree, PrintFromType, + ElideType, ShowColors); + TD.DiffTemplate(); + return TD.MakeString(S); +} diff --git a/lib/Basic/Diagnostic.cpp b/lib/Basic/Diagnostic.cpp index dbcf74529c..6bf3102b1d 100644 --- a/lib/Basic/Diagnostic.cpp +++ b/lib/Basic/Diagnostic.cpp @@ -48,6 +48,9 @@ DiagnosticsEngine::DiagnosticsEngine( ErrorsAsFatal = false; SuppressSystemWarnings = false; SuppressAllDiagnostics = false; + ElideType = true; + PrintTemplateTree = false; + ShowColors = false; ShowOverloads = Ovl_All; ExtBehavior = Ext_Ignore; @@ -660,6 +663,8 @@ FormatDiagnostic(const char *DiagStr, const char *DiagEnd, /// QualTypeVals - Pass a vector of arrays so that QualType names can be /// compared to see if more information is needed to be printed. SmallVector QualTypeVals; + SmallVector Tree; + for (unsigned i = 0, e = getNumArgs(); i < e; ++i) if (getArgKind(i) == DiagnosticsEngine::ak_qualtype) QualTypeVals.push_back(getRawArg(i)); @@ -711,7 +716,20 @@ FormatDiagnostic(const char *DiagStr, const char *DiagEnd, assert(isdigit(*DiagStr) && "Invalid format for argument in diagnostic"); unsigned ArgNo = *DiagStr++ - '0'; + // Only used for type diffing. + unsigned ArgNo2 = ArgNo; + DiagnosticsEngine::ArgumentKind Kind = getArgKind(ArgNo); + if (Kind == DiagnosticsEngine::ak_qualtype && + ModifierIs(Modifier, ModifierLen, "diff")) { + Kind = DiagnosticsEngine::ak_qualtype_pair; + assert(*DiagStr == ',' && isdigit(*(DiagStr + 1)) && + "Invalid format for diff modifier"); + ++DiagStr; // Comma. + ArgNo2 = *DiagStr++ - '0'; + assert(getArgKind(ArgNo2) == DiagnosticsEngine::ak_qualtype && + "Second value of type diff must be a qualtype"); + } switch (Kind) { // ---- STRINGS ---- @@ -796,18 +814,77 @@ FormatDiagnostic(const char *DiagStr, const char *DiagEnd, FormattedArgs.data(), FormattedArgs.size(), OutStr, QualTypeVals); break; + case DiagnosticsEngine::ak_qualtype_pair: + // Create a struct with all the info needed for printing. + TemplateDiffTypes TDT; + TDT.FromType = getRawArg(ArgNo); + TDT.ToType = getRawArg(ArgNo2); + TDT.ElideType = getDiags()->ElideType; + TDT.ShowColors = getDiags()->ShowColors; + intptr_t val = reinterpret_cast(&TDT); + + // Print the tree. + if (getDiags()->PrintTemplateTree) { + TDT.PrintFromType = true; + TDT.PrintTree = true; + getDiags()->ConvertArgToString(Kind, val, + Modifier, ModifierLen, + Argument, ArgumentLen, + FormattedArgs.data(), + FormattedArgs.size(), + Tree, QualTypeVals); + // If there is no tree information, fall back to regular printing. + if (!Tree.empty()) + break; + } + + // Non-tree printing, also the fall-back when tree printing fails. + // The fall-back is triggered when the types compared are not templates. + const char *ArgumentEnd = Argument + ArgumentLen; + const char *FirstPipe = ScanFormat(Argument, ArgumentEnd, '|'); + const char *SecondPipe = ScanFormat(FirstPipe + 1, ArgumentEnd, '|'); + + // Append before text + FormatDiagnostic(Argument, FirstPipe, OutStr); + + // Append first type + TDT.PrintTree = false; + TDT.PrintFromType = true; + getDiags()->ConvertArgToString(Kind, val, + Modifier, ModifierLen, + Argument, ArgumentLen, + FormattedArgs.data(), FormattedArgs.size(), + OutStr, QualTypeVals); + // Append middle text + FormatDiagnostic(FirstPipe + 1, SecondPipe, OutStr); + + // Append second type + TDT.PrintFromType = false; + getDiags()->ConvertArgToString(Kind, val, + Modifier, ModifierLen, + Argument, ArgumentLen, + FormattedArgs.data(), FormattedArgs.size(), + OutStr, QualTypeVals); + // Append end text + FormatDiagnostic(SecondPipe + 1, ArgumentEnd, OutStr); + break; } // Remember this argument info for subsequent formatting operations. Turn // std::strings into a null terminated string to make it be the same case as // all the other ones. - if (Kind != DiagnosticsEngine::ak_std_string) + if (Kind == DiagnosticsEngine::ak_qualtype_pair) + continue; + else if (Kind != DiagnosticsEngine::ak_std_string) FormattedArgs.push_back(std::make_pair(Kind, getRawArg(ArgNo))); else FormattedArgs.push_back(std::make_pair(DiagnosticsEngine::ak_c_string, (intptr_t)getArgStdStr(ArgNo).c_str())); } + + // Append the type tree to the end of the diagnostics. + OutStr.append(Tree.begin(), Tree.end()); } StoredDiagnostic::StoredDiagnostic() { } diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 27a222a840..307856a1bb 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -2203,6 +2203,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.AddLastArg(CmdArgs, options::OPT_fno_limit_debug_info); Args.AddLastArg(CmdArgs, options::OPT_fno_operator_names); Args.AddLastArg(CmdArgs, options::OPT_faltivec); + Args.AddLastArg(CmdArgs, options::OPT_fdiagnostics_show_template_tree); + Args.AddLastArg(CmdArgs, options::OPT_fno_elide_type); // Report and error for -faltivec on anything other then PowerPC. if (const Arg *A = Args.getLastArg(options::OPT_faltivec)) diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 8a95c80e49..40328d4200 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1349,6 +1349,8 @@ bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, Opts.ShowSourceRanges = Args.hasArg(OPT_fdiagnostics_print_source_range_info); Opts.ShowParseableFixits = Args.hasArg(OPT_fdiagnostics_parseable_fixits); Opts.VerifyDiagnostics = Args.hasArg(OPT_verify); + Opts.ElideType = !Args.hasArg(OPT_fno_elide_type); + Opts.ShowTemplateTree = Args.hasArg(OPT_fdiagnostics_show_template_tree); Opts.ErrorLimit = Args.getLastArgIntValue(OPT_ferror_limit, 0, Diags); Opts.MacroBacktraceLimit = Args.getLastArgIntValue(OPT_fmacro_backtrace_limit, diff --git a/lib/Frontend/TextDiagnostic.cpp b/lib/Frontend/TextDiagnostic.cpp index d0c3626f3c..8b7332dbeb 100644 --- a/lib/Frontend/TextDiagnostic.cpp +++ b/lib/Frontend/TextDiagnostic.cpp @@ -31,12 +31,29 @@ static const enum raw_ostream::Colors caretColor = raw_ostream::GREEN; static const enum raw_ostream::Colors warningColor = raw_ostream::MAGENTA; +static const enum raw_ostream::Colors templateColor = + raw_ostream::CYAN; static const enum raw_ostream::Colors errorColor = raw_ostream::RED; static const enum raw_ostream::Colors fatalColor = raw_ostream::RED; // Used for changing only the bold attribute. static const enum raw_ostream::Colors savedColor = raw_ostream::SAVEDCOLOR; +/// \brief Add highlights to differences in template strings. +static void applyTemplateHighlighting(raw_ostream &OS, StringRef Str, + bool &Normal) { + for (unsigned i = 0, e = Str.size(); i < e; ++i) + if (Str[i] != ToggleHighlight) { + OS << Str[i]; + } else { + if (Normal) + OS.changeColor(templateColor, true); + else + OS.resetColor(); + Normal = !Normal; + } +} + /// \brief Number of spaces to indent when word-wrapping. const unsigned WordWrapIndentation = 6; @@ -578,6 +595,7 @@ static bool printWordWrapped(raw_ostream &OS, StringRef Str, unsigned Column = 0, unsigned Indentation = WordWrapIndentation) { const unsigned Length = std::min(Str.find('\n'), Str.size()); + bool TextNormal = true; // The string used to indent each line. SmallString<16> IndentStr; @@ -601,7 +619,8 @@ static bool printWordWrapped(raw_ostream &OS, StringRef Str, OS << ' '; Column += 1; } - OS << Str.substr(WordStart, WordLength); + applyTemplateHighlighting(OS, Str.substr(WordStart, WordLength), + TextNormal); Column += WordLength; continue; } @@ -610,13 +629,16 @@ static bool printWordWrapped(raw_ostream &OS, StringRef Str, // line. OS << '\n'; OS.write(&IndentStr[0], Indentation); - OS << Str.substr(WordStart, WordLength); + applyTemplateHighlighting(OS, Str.substr(WordStart, WordLength), + TextNormal); Column = Indentation + WordLength; Wrapped = true; } // Append any remaning text from the message with its existing formatting. - OS << Str.substr(Length); + applyTemplateHighlighting(OS, Str.substr(Length), TextNormal); + + assert(TextNormal && "Text highlighted at end of diagnostic message."); return Wrapped; } diff --git a/lib/Frontend/Warnings.cpp b/lib/Frontend/Warnings.cpp index 24b7acacfd..b7d4a3b925 100644 --- a/lib/Frontend/Warnings.cpp +++ b/lib/Frontend/Warnings.cpp @@ -53,7 +53,11 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags, Diags.setIgnoreAllWarnings(Opts.IgnoreWarnings); Diags.setShowOverloads( static_cast(Opts.ShowOverloads)); - + + Diags.setElideType(Opts.ElideType); + Diags.setPrintTemplateTree(Opts.ShowTemplateTree); + Diags.setShowColors(Opts.ShowColors); + // Handle -ferror-limit if (Opts.ErrorLimit) Diags.setErrorLimit(Opts.ErrorLimit); diff --git a/test/Misc/diag-aka-types.cpp b/test/Misc/diag-aka-types.cpp index 4c9a7312cd..e82c62d3ce 100644 --- a/test/Misc/diag-aka-types.cpp +++ b/test/Misc/diag-aka-types.cpp @@ -30,27 +30,6 @@ 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'}} - } -} - namespace ns { struct str { static void method(struct data *) {} diff --git a/test/Misc/diag-template-diffing.cpp b/test/Misc/diag-template-diffing.cpp new file mode 100644 index 0000000000..9b5e6951e3 --- /dev/null +++ b/test/Misc/diag-template-diffing.cpp @@ -0,0 +1,433 @@ +// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 2>&1 | FileCheck %s -check-prefix=CHECK-ELIDE-NOTREE +// RUN: %clang_cc1 -fsyntax-only %s -fno-elide-type -std=c++11 2>&1 | FileCheck %s -check-prefix=CHECK-NOELIDE-NOTREE +// RUN: %clang_cc1 -fsyntax-only %s -fdiagnostics-show-template-tree -std=c++11 2>&1 | FileCheck %s -check-prefix=CHECK-ELIDE-TREE +// RUN: %clang_cc1 -fsyntax-only %s -fno-elide-type -fdiagnostics-show-template-tree -std=c++11 2>&1 | FileCheck %s -check-prefix=CHECK-NOELIDE-TREE + +// 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); + +namespace std { + class basic_string; + typedef basic_string string; + template class vector {}; + void g() { + vector v; + f(v); + } +} // end namespace std +// CHECK-ELIDE-NOTREE: no matching function for call to 'f' +// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'vector' to 'vector' for 1st argument +// CHECK-NOELIDE-NOTREE: no matching function for call to 'f' +// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'vector' to 'vector' for 1st argument +// CHECK-ELIDE-TREE: no matching function for call to 'f' +// CHECK-ELIDE-TREE: candidate function not viable: no known conversion for 1st argument +// CHECK-ELIDE-TREE: vector< +// CHECK-ELIDE-TREE: [class std::basic_string != class versa_string]> +// CHECK-NOELIDE-TREE: no matching function for call to 'f' +// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion for 1st argument +// CHECK-NOELIDE-TREE: vector< +// CHECK-NOELIDE-TREE: [class std::basic_string != class versa_string]> + +template +class I1{}; +void set1(I1<1,2,3,4,2,3,4,3>) {}; +void test1() { + set1(I1<1,2,3,4,2,2,4,3,7>()); +} +// CHECK-ELIDE-NOTREE: no matching function for call to 'set1' +// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'I1<[5 * ...], 2, [2 * ...], 7>' to 'I1<[5 * ...], 3, [2 * ...], (no argument)>' for 1st argument +// CHECK-NOELIDE-NOTREE: no matching function for call to 'set1' +// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'I1<1, 2, 3, 4, 2, 2, 4, 3, 7>' to 'I1<1, 2, 3, 4, 2, 3, 4, 3, (no argument)>' for 1st argument +// CHECK-ELIDE-TREE: no matching function for call to 'set1' +// CHECK-ELIDE-TREE: candidate function not viable: no known conversion for 1st argument +// CHECK-ELIDE-TREE: I1< +// CHECK-ELIDE-TREE: [5 * ...], +// CHECK-ELIDE-TREE: [2 != 3], +// CHECK-ELIDE-TREE: [2 * ...], +// CHECK-ELIDE-TREE: [7 != (no argument)]> +// CHECK-NOELIDE-TREE: no matching function for call to 'set1' +// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion for 1st argument +// CHECK-NOELIDE-TREE: I1< +// CHECK-NOELIDE-TREE: 1, +// CHECK-NOELIDE-TREE: 2, +// CHECK-NOELIDE-TREE: 3, +// CHECK-NOELIDE-TREE: 4, +// CHECK-NOELIDE-TREE: 2, +// CHECK-NOELIDE-TREE: [2 != 3], +// CHECK-NOELIDE-TREE: 4, +// CHECK-NOELIDE-TREE: 3, +// CHECK-NOELIDE-TREE: [7 != (no argument)]> + +template +class I2{}; +void set2(I2) {}; +void test2() { + set2(I2()); +} +// CHECK-ELIDE-NOTREE: no matching function for call to 'set2' +// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'I2' to 'I2' for 1st argument +// CHECK-NOELIDE-NOTREE: no matching function for call to 'set2' +// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'I2' to 'I2' for 1st argument +// CHECK-ELIDE-TREE: no matching function for call to 'set2' +// CHECK-ELIDE-TREE: candidate function not viable: no known conversion for 1st argument +// CHECK-ELIDE-TREE: I2< +// CHECK-ELIDE-TREE: [double != int], +// CHECK-ELIDE-TREE: [...], +// CHECK-ELIDE-TREE: [int != (default) void]> +// CHECK-NOELIDE-TREE: no matching function for call to 'set2' +// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion for 1st argument +// CHECK-NOELIDE-TREE: I2< +// CHECK-NOELIDE-TREE: [double != int], +// CHECK-NOELIDE-TREE: int, +// CHECK-NOELIDE-TREE: [int != (default) void]> + +int V1, V2, V3; +template +class I3{}; +void set3(I3<&V1, &V2>) {}; +void test3() { + set3(I3<&V3, &V2>()); +} +// CHECK-ELIDE-NOTREE: no matching function for call to 'set3' +// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'I3<&V3, [...]>' to 'I3<&V1, [...]>' for 1st argument +// CHECK-NOELIDE-NOTREE: no matching function for call to 'set3' +// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'I3<&V3, &V2>' to 'I3<&V1, &V2>' for 1st argument +// CHECK-ELIDE-TREE: no matching function for call to 'set3' +// CHECK-ELIDE-TREE: candidate function not viable: no known conversion for 1st argument +// CHECK-ELIDE-TREE: I3< +// CHECK-ELIDE-TREE: [&V3 != &V1] +// CHECK-ELIDE-TREE: [...]> +// CHECK-NOELIDE-TREE: no matching function for call to 'set3' +// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion for 1st argument +// CHECK-NOELIDE-TREE: I3< +// CHECK-NOELIDE-TREE: [&V3 != &V1] +// CHECK-NOELIDE-TREE: &V2> + +template +class Alpha{}; +template +class Beta{}; +template +class Gamma{}; +template +class Delta{}; + +void set4(Alpha); +void test4() { + set4(Beta()); +} +// CHECK-ELIDE-NOTREE: no matching function for call to 'set4' +// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'Beta' to 'Alpha' for 1st argument +// CHECK-NOELIDE-NOTREE: no matching function for call to 'set4' +// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'Beta' to 'Alpha' for 1st argument +// CHECK-ELIDE-TREE: no matching function for call to 'set4' +// CHECK-ELIDE-TREE: candidate function not viable: no known conversion from 'Beta' to 'Alpha' for 1st argument +// CHECK-NOELIDE-TREE: no matching function for call to 'set4' +// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion from 'Beta' to 'Alpha' for 1st argument + +void set5(Alpha, int>, int>, int>); +void test5() { + set5(Alpha, double>, double>()); +} +// CHECK-ELIDE-NOTREE: no matching function for call to 'set5' +// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'Alpha, double>, double>' to 'Alpha, int>, int>, int>' for 1st argument +// CHECK-NOELIDE-NOTREE: no matching function for call to 'set5' +// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'Alpha, double>, double>' to 'Alpha, int>, int>, int>' for 1st argument +// CHECK-ELIDE-TREE: no matching function for call to 'set5' +// CHECK-ELIDE-TREE: candidate function not viable: no known conversion for 1st argument +// CHECK-ELIDE-TREE: Alpha< +// CHECK-ELIDE-TREE: Beta< +// CHECK-ELIDE-TREE: Gamma< +// CHECK-ELIDE-TREE: [void != Delta], +// CHECK-ELIDE-TREE: [void != int]> +// CHECK-ELIDE-TREE: [double != int]> +// CHECK-ELIDE-TREE: [double != int]> +// CHECK-NOELIDE-TREE: no matching function for call to 'set5' +// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion for 1st argument +// CHECK-NOELIDE-TREE: Alpha< +// CHECK-NOELIDE-TREE: Beta< +// CHECK-NOELIDE-TREE: Gamma< +// CHECK-NOELIDE-TREE: [void != Delta], +// CHECK-NOELIDE-TREE: [void != int]> +// CHECK-NOELIDE-TREE: [double != int]> +// CHECK-NOELIDE-TREE: [double != int]> + +void test6() { + set5(Alpha, int>, int>()); +} +// CHECK-ELIDE-NOTREE: no matching function for call to 'set5' +// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'Alpha, [...]>, [...]>' to 'Alpha, int>, [...]>, [...]>' for 1st argument +// CHECK-NOELIDE-NOTREE: no matching function for call to 'set5' +// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'Alpha, int>, int>' to 'Alpha, int>, int>, int>' for 1st argument +// CHECK-ELIDE-TREE: no matching function for call to 'set5' +// CHECK-ELIDE-TREE: candidate function not viable: no known conversion for 1st argument +// CHECK-ELIDE-TREE: Alpha< +// CHECK-ELIDE-TREE: Beta< +// CHECK-ELIDE-TREE: [Delta != Gamma, int>], +// CHECK-ELIDE-TREE: [...]> +// CHECK-ELIDE-TREE: [...]> +// CHECK-NOELIDE-TREE: no matching function for call to 'set5' +// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion for 1st argument +// CHECK-NOELIDE-TREE: Alpha< +// CHECK-NOELIDE-TREE: Beta< +// CHECK-NOELIDE-TREE: [Delta != Gamma, int>], +// CHECK-NOELIDE-TREE: int> +// CHECK-NOELIDE-TREE: int> + +int a7, b7; +int c7[] = {1,2,3}; +template +class class7 {}; +void set7(class7<&a7> A) {} +void test7() { + set7(class7<&a7>()); + set7(class7<&b7>()); + set7(class7()); + set7(class7()); +} +// CHECK-ELIDE-NOTREE: no matching function for call to 'set7' +// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'class7<&b7>' to 'class7<&a7>' for 1st argument; +// CHECK-ELIDE-NOTREE: no matching function for call to 'set7' +// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'class7' to 'class7<&a7>' for 1st argument; +// CHECK-ELIDE-NOTREE: no matching function for call to 'set7' +// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'class7' to 'class7<&a7>' for 1st argument; +// CHECK-NOELIDE-NOTREE: no matching function for call to 'set7' +// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'class7<&b7>' to 'class7<&a7>' for 1st argument; +// CHECK-NOELIDE-NOTREE: no matching function for call to 'set7' +// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'class7' to 'class7<&a7>' for 1st argument; +// CHECK-NOELIDE-NOTREE: no matching function for call to 'set7' +// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'class7' to 'class7<&a7>' for 1st argument; +// CHECK-ELIDE-TREE: no matching function for call to 'set7' +// CHECK-ELIDE-TREE: candidate function not viable: no known conversion for 1st argument; +// CHECK-ELIDE-TREE: class7< +// CHECK-ELIDE-TREE: [&b7 != &a7]> +// CHECK-ELIDE-TREE: no matching function for call to 'set7' +// CHECK-ELIDE-TREE: candidate function not viable: no known conversion for 1st argument; +// CHECK-ELIDE-TREE: class7< +// CHECK-ELIDE-TREE: [c7 != &a7]> +// CHECK-ELIDE-TREE: no matching function for call to 'set7' +// CHECK-ELIDE-TREE: candidate function not viable: no known conversion for 1st argument; +// CHECK-ELIDE-TREE: class7< +// CHECK-ELIDE-TREE: [nullptr != &a7]> +// CHECK-NOELIDE-TREE: no matching function for call to 'set7' +// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion for 1st argument; +// CHECK-NOELIDE-TREE: class7< +// CHECK-NOELIDE-TREE: [&b7 != &a7]> +// CHECK-NOELIDE-TREE: no matching function for call to 'set7' +// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion for 1st argument; +// CHECK-NOELIDE-TREE: class7< +// CHECK-NOELIDE-TREE: [c7 != &a7]> +// CHECK-NOELIDE-TREE: no matching function for call to 'set7' +// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion for 1st argument; +// CHECK-NOELIDE-TREE: class7< +// CHECK-NOELIDE-TREE: [nullptr != &a7]> + +template struct S8 {}; +template using U8 = S8; +int f8(S8); +int k8 = f8(U8()); +// CHECK-ELIDE-NOTREE: no matching function for call to 'f8' +// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'S8<[2 * ...], char>' to 'S8<[2 * ...], double>' for 1st argument; +// CHECK-NOELIDE-NOTREE: no matching function for call to 'f8' +// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'S8' to 'S8' for 1st argument; +// CHECK-ELIDE-TREE: no matching function for call to 'f8' +// CHECK-ELIDE-TREE: candidate function not viable: no known conversion for 1st argument; +// CHECK-ELIDE-TREE: S8< +// CHECK-ELIDE-TREE: [2 * ...], +// CHECK-ELIDE-TREE: [char != double]> +// CHECK-NOELIDE-TREE: no matching function for call to 'f8' +// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion for 1st argument; +// CHECK-NOELIDE-TREE: S8< +// CHECK-NOELIDE-TREE: int, +// CHECK-NOELIDE-TREE: char, +// CHECK-NOELIDE-TREE: [char != double]> + +template struct S9 {}; +template using U9 = S9; +template using V9 = U9>; +int f9(S9>); +int k9 = f9(V9()); + +// CHECK-ELIDE-NOTREE: no matching function for call to 'f9' +// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'S9<[2 * ...], S9<[2 * ...], double>>' to 'S9<[2 * ...], S9<[2 * ...], const double>>' for 1st argument; +// CHECK-NOELIDE-NOTREE: no matching function for call to 'f9' +// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'S9>' to 'S9>' for 1st argument; +// CHECK-ELIDE-TREE: no matching function for call to 'f9' +// CHECK-ELIDE-TREE: candidate function not viable: no known conversion for 1st argument; +// CHECK-ELIDE-TREE: S9< +// CHECK-ELIDE-TREE: [2 * ...], +// CHECK-ELIDE-TREE: S9< +// CHECK-ELIDE-TREE: [2 * ...], +// CHECK-ELIDE-TREE: [double != const double]>> +// CHECK-NOELIDE-TREE: no matching function for call to 'f9' +// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion for 1st argument; +// CHECK-NOELIDE-TREE: S9< +// CHECK-NOELIDE-TREE: int, +// CHECK-NOELIDE-TREE: char, +// CHECK-NOELIDE-TREE: S9< +// CHECK-NOELIDE-TREE: int, +// CHECK-NOELIDE-TREE: char, +// CHECK-NOELIDE-TREE: [double != const double]>> + +template class class_types {}; +void set10(class_types) {} +void test10() { + set10(class_types()); + set10(class_types()); +} + +// CHECK-ELIDE-NOTREE: no matching function for call to 'set10' +// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'class_types<[...], (no argument)>' to 'class_types<[...], int>' for 1st argument; +// CHECK-ELIDE-NOTREE: no matching function for call to 'set10' +// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'class_types<[2 * ...], int>' to 'class_types<[2 * ...], (no argument)>' for 1st argument; +// CHECK-NOELIDE-NOTREE: no matching function for call to 'set10' +// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'class_types' to 'class_types' for 1st argument; +// CHECK-NOELIDE-NOTREE: no matching function for call to 'set10' +// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'class_types' to 'class_types' for 1st argument; +// CHECK-ELIDE-TREE: no matching function for call to 'set10' +// CHECK-ELIDE-TREE: candidate function not viable: no known conversion for 1st argument; +// CHECK-ELIDE-TREE: class_types< +// CHECK-ELIDE-TREE: [...], +// CHECK-ELIDE-TREE: [(no argument) != int]> +// CHECK-ELIDE-TREE: no matching function for call to 'set10' +// CHECK-ELIDE-TREE: candidate function not viable: no known conversion for 1st argument; +// CHECK-ELIDE-TREE: class_types< +// CHECK-ELIDE-TREE: [2 * ...], +// CHECK-ELIDE-TREE: [int != (no argument)]> +// CHECK-NOELIDE-TREE: no matching function for call to 'set10' +// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion for 1st argument; +// CHECK-NOELIDE-TREE: class_types< +// CHECK-NOELIDE-TREE: int, +// CHECK-NOELIDE-TREE: [(no argument) != int]> +// CHECK-NOELIDE-TREE: no matching function for call to 'set10' +// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion for 1st argument; +// CHECK-NOELIDE-TREE: class_types< +// CHECK-NOELIDE-TREE: int, +// CHECK-NOELIDE-TREE: int, +// CHECK-NOELIDE-TREE: [int != (no argument)]> + +template class class_ints {}; +void set11(class_ints<2, 3>) {} +void test11() { + set11(class_ints<1>()); + set11(class_ints<0, 3, 6>()); +} +// CHECK-ELIDE-NOTREE: no matching function for call to 'set11' +// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'class_ints<1, (no argument)>' to 'class_ints<2, 3>' for 1st argument; +// CHECK-ELIDE-NOTREE: no matching function for call to 'set11' +// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'class_ints<0, [...], 6>' to 'class_ints<2, [...], (no argument)>' for 1st argument; +// CHECK-NOELIDE-NOTREE: no matching function for call to 'set11' +// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'class_ints<1, (no argument)>' to 'class_ints<2, 3>' for 1st argument; +// CHECK-NOELIDE-NOTREE: no matching function for call to 'set11' +// CHECK-NOELIDE-NOTREE: candidate function not viable: no known conversion from 'class_ints<0, 3, 6>' to 'class_ints<2, 3, (no argument)>' for 1st argument; +// CHECK-ELIDE-TREE: no matching function for call to 'set11' +// CHECK-ELIDE-TREE: candidate function not viable: no known conversion for 1st argument; +// CHECK-ELIDE-TREE: class_ints< +// CHECK-ELIDE-TREE: [1 != 2], +// CHECK-ELIDE-TREE: [(no argument) != 3]> +// CHECK-ELIDE-TREE: no matching function for call to 'set11' +// CHECK-ELIDE-TREE: candidate function not viable: no known conversion for 1st argument; +// CHECK-ELIDE-TREE: class_ints< +// CHECK-ELIDE-TREE: [0 != 2], +// CHECK-ELIDE-TREE: [...], +// CHECK-ELIDE-TREE: [6 != (no argument)]> +// CHECK-NOELIDE-TREE: no matching function for call to 'set11' +// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion for 1st argument; +// CHECK-NOELIDE-TREE: class_ints< +// CHECK-NOELIDE-TREE: [1 != 2], +// CHECK-NOELIDE-TREE: [(no argument) != 3]> +// CHECK-NOELIDE-TREE: no matching function for call to 'set11' +// CHECK-NOELIDE-TREE: candidate function not viable: no known conversion for 1st argument; +// CHECK-NOELIDE-TREE: class_ints< +// CHECK-NOELIDE-TREE: [0 != 2], +// CHECK-NOELIDE-TREE: 3, +// CHECK-NOELIDE-TREE: [6 != (no argument)]> + +template class ...A> class class_template_templates {}; +template class tt1 {}; +template class tt2 {}; +void set12(class_template_templates) {} +void test12() { + set12(class_template_templates()); + set12(class_template_templates()); +} +// CHECK-ELIDE-NOTREE: no matching function for call to 'set12' +// CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'class_template_templates