//===--- ASTDiagnostic.cpp - Diagnostic Printing Hooks for AST Nodes ------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements a diagnostic formatting hook for AST elements. // //===----------------------------------------------------------------------===// #include "clang/AST/ASTDiagnostic.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTLambda.h" #include "clang/AST/Attr.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/TemplateBase.h" #include "clang/AST/Type.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/raw_ostream.h" using namespace clang; // Returns a desugared version of the QualType, and marks ShouldAKA as true // whenever we remove significant sugar from the type. Make sure ShouldAKA // is initialized before passing it in. QualType clang::desugarForDiagnostic(ASTContext &Context, QualType QT, bool &ShouldAKA) { QualifierCollector QC; while (true) { const Type *Ty = QC.strip(QT); // Don't aka just because we saw an elaborated type... if (const ElaboratedType *ET = dyn_cast(Ty)) { QT = ET->desugar(); continue; } // ... or a using type ... if (const UsingType *UT = dyn_cast(Ty)) { QT = UT->desugar(); continue; } // ... or a paren type ... if (const ParenType *PT = dyn_cast(Ty)) { QT = PT->desugar(); continue; } // ... or a macro defined type ... if (const MacroQualifiedType *MDT = dyn_cast(Ty)) { QT = MDT->desugar(); continue; } // ...or a substituted template type parameter ... if (const SubstTemplateTypeParmType *ST = dyn_cast(Ty)) { QT = ST->desugar(); continue; } // ...or an attributed type... if (const AttributedType *AT = dyn_cast(Ty)) { QT = AT->desugar(); continue; } // ...or an adjusted type... if (const AdjustedType *AT = dyn_cast(Ty)) { QT = AT->desugar(); continue; } // ... or an auto type. if (const AutoType *AT = dyn_cast(Ty)) { if (!AT->isSugared()) break; QT = AT->desugar(); continue; } // Desugar FunctionType if return type or any parameter type should be // desugared. Preserve nullability attribute on desugared types. if (const FunctionType *FT = dyn_cast(Ty)) { bool DesugarReturn = false; QualType SugarRT = FT->getReturnType(); QualType RT = desugarForDiagnostic(Context, SugarRT, DesugarReturn); if (auto nullability = AttributedType::stripOuterNullability(SugarRT)) { RT = Context.getAttributedType( AttributedType::getNullabilityAttrKind(*nullability), RT, RT); } bool DesugarArgument = false; SmallVector Args; const FunctionProtoType *FPT = dyn_cast(FT); if (FPT) { for (QualType SugarPT : FPT->param_types()) { QualType PT = desugarForDiagnostic(Context, SugarPT, DesugarArgument); if (auto nullability = AttributedType::stripOuterNullability(SugarPT)) { PT = Context.getAttributedType( AttributedType::getNullabilityAttrKind(*nullability), PT, PT); } Args.push_back(PT); } } if (DesugarReturn || DesugarArgument) { ShouldAKA = true; QT = FPT ? Context.getFunctionType(RT, Args, FPT->getExtProtoInfo()) : Context.getFunctionNoProtoType(RT, FT->getExtInfo()); break; } } // Desugar template specializations if any template argument should be // desugared. if (const TemplateSpecializationType *TST = dyn_cast(Ty)) { if (!TST->isTypeAlias()) { bool DesugarArgument = false; SmallVector Args; for (const TemplateArgument &Arg : TST->template_arguments()) { if (Arg.getKind() == TemplateArgument::Type) Args.push_back(desugarForDiagnostic(Context, Arg.getAsType(), DesugarArgument)); else Args.push_back(Arg); } if (DesugarArgument) { ShouldAKA = true; QT = Context.getTemplateSpecializationType( TST->getTemplateName(), Args, QT); } break; } } if (const auto *AT = dyn_cast(Ty)) { QualType ElementTy = desugarForDiagnostic(Context, AT->getElementType(), ShouldAKA); if (const auto *CAT = dyn_cast(AT)) QT = Context.getConstantArrayType( ElementTy, CAT->getSize(), CAT->getSizeExpr(), CAT->getSizeModifier(), CAT->getIndexTypeCVRQualifiers()); else if (const auto *VAT = dyn_cast(AT)) QT = Context.getVariableArrayType( ElementTy, VAT->getSizeExpr(), VAT->getSizeModifier(), VAT->getIndexTypeCVRQualifiers(), VAT->getBracketsRange()); else if (const auto *DSAT = dyn_cast(AT)) QT = Context.getDependentSizedArrayType( ElementTy, DSAT->getSizeExpr(), DSAT->getSizeModifier(), DSAT->getIndexTypeCVRQualifiers(), DSAT->getBracketsRange()); else if (const auto *IAT = dyn_cast(AT)) QT = Context.getIncompleteArrayType(ElementTy, IAT->getSizeModifier(), IAT->getIndexTypeCVRQualifiers()); else llvm_unreachable("Unhandled array type"); 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() || QualType(Ty, 0) == Context.getBuiltinMSVaListType()) break; // Otherwise, do a single-step desugar. QualType Underlying; bool IsSugar = false; switch (Ty->getTypeClass()) { #define ABSTRACT_TYPE(Class, Base) #define TYPE(Class, Base) \ case Type::Class: { \ const Class##Type *CTy = cast(Ty); \ if (CTy->isSugared()) { \ IsSugar = true; \ Underlying = CTy->desugar(); \ } \ break; \ } #include "clang/AST/TypeNodes.inc" } // If it wasn't sugared, we're done. if (!IsSugar) break; // If the desugared type is a vector type, we don't want to expand // it, it will turn into an attribute mess. People want their "vec4". if (isa(Underlying)) break; // Don't desugar through the primary typedef of an anonymous type. if (const TagType *UTT = Underlying->getAs()) if (const TypedefType *QTT = dyn_cast(QT)) if (UTT->getDecl()->getTypedefNameForAnonDecl() == QTT->getDecl()) break; // Record that we actually looked through an opaque type here. ShouldAKA = true; QT = Underlying; } // If we have a pointer-like type, desugar the pointee as well. // FIXME: Handle other pointer-like types. if (const PointerType *Ty = QT->getAs()) { QT = Context.getPointerType( desugarForDiagnostic(Context, Ty->getPointeeType(), ShouldAKA)); } else if (const auto *Ty = QT->getAs()) { QT = Context.getObjCObjectPointerType( desugarForDiagnostic(Context, Ty->getPointeeType(), ShouldAKA)); } else if (const LValueReferenceType *Ty = QT->getAs()) { QT = Context.getLValueReferenceType( desugarForDiagnostic(Context, Ty->getPointeeType(), ShouldAKA)); } else if (const RValueReferenceType *Ty = QT->getAs()) { QT = Context.getRValueReferenceType( desugarForDiagnostic(Context, Ty->getPointeeType(), ShouldAKA)); } else if (const auto *Ty = QT->getAs()) { if (Ty->getBaseType().getTypePtr() != Ty && !ShouldAKA) { QualType BaseType = desugarForDiagnostic(Context, Ty->getBaseType(), ShouldAKA); QT = Context.getObjCObjectType( BaseType, Ty->getTypeArgsAsWritten(), llvm::ArrayRef(Ty->qual_begin(), Ty->getNumProtocols()), Ty->isKindOfTypeAsWritten()); } } return QC.apply(Context, QT); } /// Convert the given type to a string suitable for printing as part of /// a diagnostic. /// /// 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 /// 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. /// 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, ArrayRef PrevArgs, ArrayRef QualTypeVals) { // FIXME: Playing with std::string is really slow. bool ForceAKA = false; QualType CanTy = Ty.getCanonicalType(); std::string S = Ty.getAsString(Context.getPrintingPolicy()); std::string CanS = CanTy.getAsString(Context.getPrintingPolicy()); for (const intptr_t &QualTypeVal : QualTypeVals) { QualType CompareTy = QualType::getFromOpaquePtr(reinterpret_cast(QualTypeVal)); if (CompareTy.isNull()) continue; if (CompareTy == Ty) continue; // Same types QualType CompareCanTy = CompareTy.getCanonicalType(); if (CompareCanTy == CanTy) continue; // Same canonical types std::string CompareS = CompareTy.getAsString(Context.getPrintingPolicy()); bool ShouldAKA = false; QualType CompareDesugar = desugarForDiagnostic(Context, CompareTy, ShouldAKA); std::string CompareDesugarStr = CompareDesugar.getAsString(Context.getPrintingPolicy()); if (CompareS != S && CompareDesugarStr != S) continue; // The type string is different than the comparison string // and the desugared comparison string. std::string CompareCanS = CompareCanTy.getAsString(Context.getPrintingPolicy()); 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. bool Repeated = false; for (const auto &PrevArg : PrevArgs) { // TODO: Handle ak_declcontext case. if (PrevArg.first == DiagnosticsEngine::ak_qualtype) { QualType PrevTy( QualType::getFromOpaquePtr(reinterpret_cast(PrevArg.second))); if (PrevTy == Ty) { Repeated = true; break; } } } // Consider producing an a.k.a. clause if removing all the direct // sugar gives us something "significantly different". if (!Repeated) { bool ShouldAKA = false; QualType DesugaredTy = desugarForDiagnostic(Context, Ty, ShouldAKA); if (ShouldAKA || ForceAKA) { if (DesugaredTy == Ty) { DesugaredTy = Ty.getCanonicalType(); } std::string akaStr = DesugaredTy.getAsString(Context.getPrintingPolicy()); if (akaStr != S) { S = "'" + S + "' (aka '" + akaStr + "')"; return S; } } // Give some additional info on vector types. These are either not desugared // or displaying complex __attribute__ expressions so add details of the // type and element count. if (const auto *VTy = Ty->getAs()) { std::string DecoratedString; llvm::raw_string_ostream OS(DecoratedString); const char *Values = VTy->getNumElements() > 1 ? "values" : "value"; OS << "'" << S << "' (vector of " << VTy->getNumElements() << " '" << VTy->getElementType().getAsString(Context.getPrintingPolicy()) << "' " << Values << ")"; return DecoratedString; } } S = "'" + S + "'"; return S; } static bool FormatTemplateTypeDiff(ASTContext &Context, QualType FromType, QualType ToType, bool PrintTree, bool PrintFromType, bool ElideType, bool ShowColors, raw_ostream &OS); void clang::FormatASTNodeDiagnosticArgument( DiagnosticsEngine::ArgumentKind Kind, intptr_t Val, StringRef Modifier, StringRef Argument, ArrayRef PrevArgs, SmallVectorImpl &Output, void *Cookie, ArrayRef QualTypeVals) { ASTContext &Context = *static_cast(Cookie); size_t OldEnd = Output.size(); llvm::raw_svector_ostream OS(Output); bool NeedQuotes = true; switch (Kind) { default: llvm_unreachable("unknown ArgumentKind"); case DiagnosticsEngine::ak_addrspace: { assert(Modifier.empty() && Argument.empty() && "Invalid modifier for Qualifiers argument"); auto S = Qualifiers::getAddrSpaceAsString(static_cast(Val)); if (S.empty()) { OS << (Context.getLangOpts().OpenCL ? "default" : "generic"); OS << " address space"; } else { OS << "address space"; OS << " '" << S << "'"; } NeedQuotes = false; break; } case DiagnosticsEngine::ak_qual: { assert(Modifier.empty() && Argument.empty() && "Invalid modifier for Qualifiers argument"); Qualifiers Q(Qualifiers::fromOpaqueValue(Val)); auto S = Q.getAsString(); if (S.empty()) { OS << "unqualified"; NeedQuotes = false; } else { OS << S; } break; } case DiagnosticsEngine::ak_qualtype_pair: { 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, OS)) { NeedQuotes = !TDT.PrintTree; TDT.TemplateDiffUsed = true; break; } // Don't fall-back during tree printing. The caller will handle // this case. if (TDT.PrintTree) return; // Attempting to do a template diff on non-templates. Set the variables // and continue with regular type printing of the appropriate type. Val = TDT.PrintFromType ? TDT.FromType : TDT.ToType; Modifier = StringRef(); Argument = StringRef(); // Fall through [[fallthrough]]; } case DiagnosticsEngine::ak_qualtype: { assert(Modifier.empty() && Argument.empty() && "Invalid modifier for QualType argument"); QualType Ty(QualType::getFromOpaquePtr(reinterpret_cast(Val))); OS << ConvertTypeToDiagnosticString(Context, Ty, PrevArgs, QualTypeVals); NeedQuotes = false; break; } case DiagnosticsEngine::ak_declarationname: { if (Modifier == "objcclass" && Argument.empty()) OS << '+'; else if (Modifier == "objcinstance" && Argument.empty()) OS << '-'; else assert(Modifier.empty() && Argument.empty() && "Invalid modifier for DeclarationName argument"); OS << DeclarationName::getFromOpaqueInteger(Val); break; } case DiagnosticsEngine::ak_nameddecl: { bool Qualified; if (Modifier == "q" && Argument.empty()) Qualified = true; else { assert(Modifier.empty() && Argument.empty() && "Invalid modifier for NamedDecl* argument"); Qualified = false; } const NamedDecl *ND = reinterpret_cast(Val); ND->getNameForDiagnostic(OS, Context.getPrintingPolicy(), Qualified); break; } case DiagnosticsEngine::ak_nestednamespec: { NestedNameSpecifier *NNS = reinterpret_cast(Val); NNS->print(OS, Context.getPrintingPolicy()); NeedQuotes = false; break; } case DiagnosticsEngine::ak_declcontext: { DeclContext *DC = reinterpret_cast (Val); assert(DC && "Should never have a null declaration context"); NeedQuotes = false; // FIXME: Get the strings for DeclContext from some localized place if (DC->isTranslationUnit()) { if (Context.getLangOpts().CPlusPlus) OS << "the global namespace"; else OS << "the global scope"; } else if (DC->isClosure()) { OS << "block literal"; } else if (isLambdaCallOperator(DC)) { OS << "lambda expression"; } else if (TypeDecl *Type = dyn_cast(DC)) { OS << ConvertTypeToDiagnosticString(Context, Context.getTypeDeclType(Type), PrevArgs, QualTypeVals); } else { assert(isa(DC) && "Expected a NamedDecl"); NamedDecl *ND = cast(DC); if (isa(ND)) OS << "namespace "; else if (isa(ND)) OS << "method "; else if (isa(ND)) OS << "function "; OS << '\''; ND->getNameForDiagnostic(OS, Context.getPrintingPolicy(), true); OS << '\''; } break; } case DiagnosticsEngine::ak_attr: { const Attr *At = reinterpret_cast(Val); assert(At && "Received null Attr object!"); OS << '\'' << At->getSpelling() << '\''; NeedQuotes = false; break; } } if (NeedQuotes) { Output.insert(Output.begin()+OldEnd, '\''); 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; /// FromTemplateType - When single type printing is selected, this is the /// type to be printed. When tree printing is selected, this type will /// show up first in the tree. QualType FromTemplateType; /// ToTemplateType - The type that FromType is compared to. Only in tree /// printing will this type be outputed. QualType ToTemplateType; /// OS - The stream used to construct the output strings. raw_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 { public: /// DiffKind - The difference in a DiffNode. Fields of /// TemplateArgumentInfo needed by each difference can be found in the /// Set* and Get* functions. enum DiffKind { /// Incomplete or invalid node. Invalid, /// Another level of templates Template, /// Type difference, all type differences except those falling under /// the Template difference. Type, /// Expression difference, this is only when both arguments are /// expressions. If one argument is an expression and the other is /// Integer or Declaration, then use that diff type instead. Expression, /// Template argument difference TemplateTemplate, /// Integer difference Integer, /// Declaration difference, nullptr arguments are included here Declaration, /// One argument being integer and the other being declaration FromIntegerAndToDeclaration, FromDeclarationAndToInteger }; private: /// TemplateArgumentInfo - All the information needed to pretty print /// a template argument. See the Set* and Get* functions to see which /// fields are used for each DiffKind. struct TemplateArgumentInfo { QualType ArgType; Qualifiers Qual; llvm::APSInt Val; bool IsValidInt = false; Expr *ArgExpr = nullptr; TemplateDecl *TD = nullptr; ValueDecl *VD = nullptr; bool NeedAddressOf = false; bool IsNullPtr = false; bool IsDefault = false; }; /// 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 { DiffKind Kind = Invalid; /// NextNode - The index of the next sibling node or 0. unsigned NextNode = 0; /// ChildNode - The index of the first child node or 0. unsigned ChildNode = 0; /// ParentNode - The index of the parent node. unsigned ParentNode = 0; TemplateArgumentInfo FromArgInfo, ToArgInfo; /// Same - Whether the two arguments evaluate to the same value. bool Same = false; DiffNode(unsigned ParentNode = 0) : ParentNode(ParentNode) {} }; /// FlatTree - A flattened tree used to store the DiffNodes. 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), ReadNode(0) { FlatTree.push_back(DiffNode()); } // Node writing functions, one for each valid DiffKind element. void SetTemplateDiff(TemplateDecl *FromTD, TemplateDecl *ToTD, Qualifiers FromQual, Qualifiers ToQual, bool FromDefault, bool ToDefault) { assert(FlatTree[CurrentNode].Kind == Invalid && "Node is not empty."); FlatTree[CurrentNode].Kind = Template; FlatTree[CurrentNode].FromArgInfo.TD = FromTD; FlatTree[CurrentNode].ToArgInfo.TD = ToTD; FlatTree[CurrentNode].FromArgInfo.Qual = FromQual; FlatTree[CurrentNode].ToArgInfo.Qual = ToQual; SetDefault(FromDefault, ToDefault); } void SetTypeDiff(QualType FromType, QualType ToType, bool FromDefault, bool ToDefault) { assert(FlatTree[CurrentNode].Kind == Invalid && "Node is not empty."); FlatTree[CurrentNode].Kind = Type; FlatTree[CurrentNode].FromArgInfo.ArgType = FromType; FlatTree[CurrentNode].ToArgInfo.ArgType = ToType; SetDefault(FromDefault, ToDefault); } void SetExpressionDiff(Expr *FromExpr, Expr *ToExpr, bool FromDefault, bool ToDefault) { assert(FlatTree[CurrentNode].Kind == Invalid && "Node is not empty."); FlatTree[CurrentNode].Kind = Expression; FlatTree[CurrentNode].FromArgInfo.ArgExpr = FromExpr; FlatTree[CurrentNode].ToArgInfo.ArgExpr = ToExpr; SetDefault(FromDefault, ToDefault); } void SetTemplateTemplateDiff(TemplateDecl *FromTD, TemplateDecl *ToTD, bool FromDefault, bool ToDefault) { assert(FlatTree[CurrentNode].Kind == Invalid && "Node is not empty."); FlatTree[CurrentNode].Kind = TemplateTemplate; FlatTree[CurrentNode].FromArgInfo.TD = FromTD; FlatTree[CurrentNode].ToArgInfo.TD = ToTD; SetDefault(FromDefault, ToDefault); } void SetIntegerDiff(const llvm::APSInt &FromInt, const llvm::APSInt &ToInt, bool IsValidFromInt, bool IsValidToInt, QualType FromIntType, QualType ToIntType, Expr *FromExpr, Expr *ToExpr, bool FromDefault, bool ToDefault) { assert(FlatTree[CurrentNode].Kind == Invalid && "Node is not empty."); FlatTree[CurrentNode].Kind = Integer; FlatTree[CurrentNode].FromArgInfo.Val = FromInt; FlatTree[CurrentNode].ToArgInfo.Val = ToInt; FlatTree[CurrentNode].FromArgInfo.IsValidInt = IsValidFromInt; FlatTree[CurrentNode].ToArgInfo.IsValidInt = IsValidToInt; FlatTree[CurrentNode].FromArgInfo.ArgType = FromIntType; FlatTree[CurrentNode].ToArgInfo.ArgType = ToIntType; FlatTree[CurrentNode].FromArgInfo.ArgExpr = FromExpr; FlatTree[CurrentNode].ToArgInfo.ArgExpr = ToExpr; SetDefault(FromDefault, ToDefault); } void SetDeclarationDiff(ValueDecl *FromValueDecl, ValueDecl *ToValueDecl, bool FromAddressOf, bool ToAddressOf, bool FromNullPtr, bool ToNullPtr, Expr *FromExpr, Expr *ToExpr, bool FromDefault, bool ToDefault) { assert(FlatTree[CurrentNode].Kind == Invalid && "Node is not empty."); FlatTree[CurrentNode].Kind = Declaration; FlatTree[CurrentNode].FromArgInfo.VD = FromValueDecl; FlatTree[CurrentNode].ToArgInfo.VD = ToValueDecl; FlatTree[CurrentNode].FromArgInfo.NeedAddressOf = FromAddressOf; FlatTree[CurrentNode].ToArgInfo.NeedAddressOf = ToAddressOf; FlatTree[CurrentNode].FromArgInfo.IsNullPtr = FromNullPtr; FlatTree[CurrentNode].ToArgInfo.IsNullPtr = ToNullPtr; FlatTree[CurrentNode].FromArgInfo.ArgExpr = FromExpr; FlatTree[CurrentNode].ToArgInfo.ArgExpr = ToExpr; SetDefault(FromDefault, ToDefault); } void SetFromDeclarationAndToIntegerDiff( ValueDecl *FromValueDecl, bool FromAddressOf, bool FromNullPtr, Expr *FromExpr, const llvm::APSInt &ToInt, bool IsValidToInt, QualType ToIntType, Expr *ToExpr, bool FromDefault, bool ToDefault) { assert(FlatTree[CurrentNode].Kind == Invalid && "Node is not empty."); FlatTree[CurrentNode].Kind = FromDeclarationAndToInteger; FlatTree[CurrentNode].FromArgInfo.VD = FromValueDecl; FlatTree[CurrentNode].FromArgInfo.NeedAddressOf = FromAddressOf; FlatTree[CurrentNode].FromArgInfo.IsNullPtr = FromNullPtr; FlatTree[CurrentNode].FromArgInfo.ArgExpr = FromExpr; FlatTree[CurrentNode].ToArgInfo.Val = ToInt; FlatTree[CurrentNode].ToArgInfo.IsValidInt = IsValidToInt; FlatTree[CurrentNode].ToArgInfo.ArgType = ToIntType; FlatTree[CurrentNode].ToArgInfo.ArgExpr = ToExpr; SetDefault(FromDefault, ToDefault); } void SetFromIntegerAndToDeclarationDiff( const llvm::APSInt &FromInt, bool IsValidFromInt, QualType FromIntType, Expr *FromExpr, ValueDecl *ToValueDecl, bool ToAddressOf, bool ToNullPtr, Expr *ToExpr, bool FromDefault, bool ToDefault) { assert(FlatTree[CurrentNode].Kind == Invalid && "Node is not empty."); FlatTree[CurrentNode].Kind = FromIntegerAndToDeclaration; FlatTree[CurrentNode].FromArgInfo.Val = FromInt; FlatTree[CurrentNode].FromArgInfo.IsValidInt = IsValidFromInt; FlatTree[CurrentNode].FromArgInfo.ArgType = FromIntType; FlatTree[CurrentNode].FromArgInfo.ArgExpr = FromExpr; FlatTree[CurrentNode].ToArgInfo.VD = ToValueDecl; FlatTree[CurrentNode].ToArgInfo.NeedAddressOf = ToAddressOf; FlatTree[CurrentNode].ToArgInfo.IsNullPtr = ToNullPtr; FlatTree[CurrentNode].ToArgInfo.ArgExpr = ToExpr; SetDefault(FromDefault, ToDefault); } /// SetDefault - Sets FromDefault and ToDefault flags of the current node. void SetDefault(bool FromDefault, bool ToDefault) { assert((!FromDefault || !ToDefault) && "Both arguments cannot be default."); FlatTree[CurrentNode].FromArgInfo.IsDefault = FromDefault; FlatTree[CurrentNode].ToArgInfo.IsDefault = ToDefault; } /// SetSame - Sets the same flag of the current node. void SetSame(bool Same) { FlatTree[CurrentNode].Same = Same; } /// SetKind - Sets the current node's type. void SetKind(DiffKind Kind) { FlatTree[CurrentNode].Kind = Kind; } /// Up - Changes the node to the parent of the current node. void Up() { assert(FlatTree[CurrentNode].Kind != Invalid && "Cannot exit node before setting node information."); CurrentNode = FlatTree[CurrentNode].ParentNode; } /// AddNode - Adds a child node to the current node, then sets that node /// node as the current node. void AddNode() { assert(FlatTree[CurrentNode].Kind == Template && "Only Template nodes can have children nodes."); 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; } void GetTemplateDiff(TemplateDecl *&FromTD, TemplateDecl *&ToTD, Qualifiers &FromQual, Qualifiers &ToQual) { assert(FlatTree[ReadNode].Kind == Template && "Unexpected kind."); FromTD = FlatTree[ReadNode].FromArgInfo.TD; ToTD = FlatTree[ReadNode].ToArgInfo.TD; FromQual = FlatTree[ReadNode].FromArgInfo.Qual; ToQual = FlatTree[ReadNode].ToArgInfo.Qual; } void GetTypeDiff(QualType &FromType, QualType &ToType) { assert(FlatTree[ReadNode].Kind == Type && "Unexpected kind"); FromType = FlatTree[ReadNode].FromArgInfo.ArgType; ToType = FlatTree[ReadNode].ToArgInfo.ArgType; } void GetExpressionDiff(Expr *&FromExpr, Expr *&ToExpr) { assert(FlatTree[ReadNode].Kind == Expression && "Unexpected kind"); FromExpr = FlatTree[ReadNode].FromArgInfo.ArgExpr; ToExpr = FlatTree[ReadNode].ToArgInfo.ArgExpr; } void GetTemplateTemplateDiff(TemplateDecl *&FromTD, TemplateDecl *&ToTD) { assert(FlatTree[ReadNode].Kind == TemplateTemplate && "Unexpected kind."); FromTD = FlatTree[ReadNode].FromArgInfo.TD; ToTD = FlatTree[ReadNode].ToArgInfo.TD; } void GetIntegerDiff(llvm::APSInt &FromInt, llvm::APSInt &ToInt, bool &IsValidFromInt, bool &IsValidToInt, QualType &FromIntType, QualType &ToIntType, Expr *&FromExpr, Expr *&ToExpr) { assert(FlatTree[ReadNode].Kind == Integer && "Unexpected kind."); FromInt = FlatTree[ReadNode].FromArgInfo.Val; ToInt = FlatTree[ReadNode].ToArgInfo.Val; IsValidFromInt = FlatTree[ReadNode].FromArgInfo.IsValidInt; IsValidToInt = FlatTree[ReadNode].ToArgInfo.IsValidInt; FromIntType = FlatTree[ReadNode].FromArgInfo.ArgType; ToIntType = FlatTree[ReadNode].ToArgInfo.ArgType; FromExpr = FlatTree[ReadNode].FromArgInfo.ArgExpr; ToExpr = FlatTree[ReadNode].ToArgInfo.ArgExpr; } void GetDeclarationDiff(ValueDecl *&FromValueDecl, ValueDecl *&ToValueDecl, bool &FromAddressOf, bool &ToAddressOf, bool &FromNullPtr, bool &ToNullPtr, Expr *&FromExpr, Expr *&ToExpr) { assert(FlatTree[ReadNode].Kind == Declaration && "Unexpected kind."); FromValueDecl = FlatTree[ReadNode].FromArgInfo.VD; ToValueDecl = FlatTree[ReadNode].ToArgInfo.VD; FromAddressOf = FlatTree[ReadNode].FromArgInfo.NeedAddressOf; ToAddressOf = FlatTree[ReadNode].ToArgInfo.NeedAddressOf; FromNullPtr = FlatTree[ReadNode].FromArgInfo.IsNullPtr; ToNullPtr = FlatTree[ReadNode].ToArgInfo.IsNullPtr; FromExpr = FlatTree[ReadNode].FromArgInfo.ArgExpr; ToExpr = FlatTree[ReadNode].ToArgInfo.ArgExpr; } void GetFromDeclarationAndToIntegerDiff( ValueDecl *&FromValueDecl, bool &FromAddressOf, bool &FromNullPtr, Expr *&FromExpr, llvm::APSInt &ToInt, bool &IsValidToInt, QualType &ToIntType, Expr *&ToExpr) { assert(FlatTree[ReadNode].Kind == FromDeclarationAndToInteger && "Unexpected kind."); FromValueDecl = FlatTree[ReadNode].FromArgInfo.VD; FromAddressOf = FlatTree[ReadNode].FromArgInfo.NeedAddressOf; FromNullPtr = FlatTree[ReadNode].FromArgInfo.IsNullPtr; FromExpr = FlatTree[ReadNode].FromArgInfo.ArgExpr; ToInt = FlatTree[ReadNode].ToArgInfo.Val; IsValidToInt = FlatTree[ReadNode].ToArgInfo.IsValidInt; ToIntType = FlatTree[ReadNode].ToArgInfo.ArgType; ToExpr = FlatTree[ReadNode].ToArgInfo.ArgExpr; } void GetFromIntegerAndToDeclarationDiff( llvm::APSInt &FromInt, bool &IsValidFromInt, QualType &FromIntType, Expr *&FromExpr, ValueDecl *&ToValueDecl, bool &ToAddressOf, bool &ToNullPtr, Expr *&ToExpr) { assert(FlatTree[ReadNode].Kind == FromIntegerAndToDeclaration && "Unexpected kind."); FromInt = FlatTree[ReadNode].FromArgInfo.Val; IsValidFromInt = FlatTree[ReadNode].FromArgInfo.IsValidInt; FromIntType = FlatTree[ReadNode].FromArgInfo.ArgType; FromExpr = FlatTree[ReadNode].FromArgInfo.ArgExpr; ToValueDecl = FlatTree[ReadNode].ToArgInfo.VD; ToAddressOf = FlatTree[ReadNode].ToArgInfo.NeedAddressOf; ToNullPtr = FlatTree[ReadNode].ToArgInfo.IsNullPtr; ToExpr = FlatTree[ReadNode].ToArgInfo.ArgExpr; } /// FromDefault - Return true if the from argument is the default. bool FromDefault() { return FlatTree[ReadNode].FromArgInfo.IsDefault; } /// ToDefault - Return true if the to argument is the default. bool ToDefault() { return FlatTree[ReadNode].ToArgInfo.IsDefault; } /// 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; } /// Empty - Returns true if the tree has no information. bool Empty() { return GetKind() == Invalid; } /// GetKind - Returns the current node's type. DiffKind GetKind() { return FlatTree[ReadNode].Kind; } }; DiffTree Tree; /// TSTiterator - a pair of iterators that walks the /// TemplateSpecializationType and the desugared TemplateSpecializationType. /// The deseguared TemplateArgument should provide the canonical argument /// for comparisons. class TSTiterator { typedef const TemplateArgument& reference; typedef const TemplateArgument* pointer; /// InternalIterator - 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 InternalIterator { /// 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; /// InternalIterator - Constructs an iterator and sets it to the first /// template argument. InternalIterator(const TemplateSpecializationType *TST) : TST(TST), Index(0), CurrentTA(nullptr), EndTA(nullptr) { if (!TST) return; if (isEnd()) return; // Set to first template argument. If not a parameter pack, done. TemplateArgument TA = TST->template_arguments()[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); } /// Return true if the iterator is non-singular. bool isValid() const { return TST; } /// isEnd - Returns true if the iterator is one past the end. bool isEnd() const { assert(TST && "InternalIterator is invalid with a null TST."); return Index >= TST->template_arguments().size(); } /// &operator++ - Increment the iterator to the next template argument. InternalIterator &operator++() { assert(TST && "InternalIterator is invalid with a null TST."); if (isEnd()) { return *this; } // 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->template_arguments().size()) break; // If the TemplateArgument is not a parameter pack, done. TemplateArgument TA = TST->template_arguments()[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(TST && "InternalIterator is invalid with a null TST."); assert(!isEnd() && "Index exceeds number of arguments."); if (CurrentTA == EndTA) return TST->template_arguments()[Index]; else return *CurrentTA; } /// operator-> - Allow access to the underlying TemplateArgument. pointer operator->() const { assert(TST && "InternalIterator is invalid with a null TST."); return &operator*(); } }; InternalIterator SugaredIterator; InternalIterator DesugaredIterator; public: TSTiterator(ASTContext &Context, const TemplateSpecializationType *TST) : SugaredIterator(TST), DesugaredIterator( (TST->isSugared() && !TST->isTypeAlias()) ? GetTemplateSpecializationType(Context, TST->desugar()) : nullptr) {} /// &operator++ - Increment the iterator to the next template argument. TSTiterator &operator++() { ++SugaredIterator; if (DesugaredIterator.isValid()) ++DesugaredIterator; return *this; } /// operator* - Returns the appropriate TemplateArgument. reference operator*() const { return *SugaredIterator; } /// operator-> - Allow access to the underlying TemplateArgument. pointer operator->() const { return &operator*(); } /// isEnd - Returns true if no more TemplateArguments are available. bool isEnd() const { return SugaredIterator.isEnd(); } /// hasDesugaredTA - Returns true if there is another TemplateArgument /// available. bool hasDesugaredTA() const { return DesugaredIterator.isValid() && !DesugaredIterator.isEnd(); } /// getDesugaredTA - Returns the desugared TemplateArgument. reference getDesugaredTA() const { assert(DesugaredIterator.isValid() && "Desugared TemplateArgument should not be used."); return *DesugaredIterator; } }; // 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; if (const auto* SubstType = Ty->getAs()) Ty = SubstType->getReplacementType(); const RecordType *RT = Ty->getAs(); if (!RT) return nullptr; const ClassTemplateSpecializationDecl *CTSD = dyn_cast(RT->getDecl()); if (!CTSD) return nullptr; Ty = Context.getTemplateSpecializationType( TemplateName(CTSD->getSpecializedTemplate()), CTSD->getTemplateArgs().asArray(), Ty.getLocalUnqualifiedType().getCanonicalType()); return Ty->getAs(); } /// Returns true if the DiffType is Type and false for Template. static bool OnlyPerformTypeDiff(ASTContext &Context, QualType FromType, QualType ToType, const TemplateSpecializationType *&FromArgTST, const TemplateSpecializationType *&ToArgTST) { if (FromType.isNull() || ToType.isNull()) return true; if (Context.hasSameType(FromType, ToType)) return true; FromArgTST = GetTemplateSpecializationType(Context, FromType); ToArgTST = GetTemplateSpecializationType(Context, ToType); if (!FromArgTST || !ToArgTST) return true; if (!hasSameTemplate(FromArgTST, ToArgTST)) return true; return false; } /// DiffTypes - Fills a DiffNode with information about a type difference. void DiffTypes(const TSTiterator &FromIter, const TSTiterator &ToIter) { QualType FromType = GetType(FromIter); QualType ToType = GetType(ToIter); bool FromDefault = FromIter.isEnd() && !FromType.isNull(); bool ToDefault = ToIter.isEnd() && !ToType.isNull(); const TemplateSpecializationType *FromArgTST = nullptr; const TemplateSpecializationType *ToArgTST = nullptr; if (OnlyPerformTypeDiff(Context, FromType, ToType, FromArgTST, ToArgTST)) { Tree.SetTypeDiff(FromType, ToType, FromDefault, ToDefault); Tree.SetSame(!FromType.isNull() && !ToType.isNull() && Context.hasSameType(FromType, ToType)); } else { assert(FromArgTST && ToArgTST && "Both template specializations need to be valid."); Qualifiers FromQual = FromType.getQualifiers(), ToQual = ToType.getQualifiers(); FromQual -= QualType(FromArgTST, 0).getQualifiers(); ToQual -= QualType(ToArgTST, 0).getQualifiers(); Tree.SetTemplateDiff(FromArgTST->getTemplateName().getAsTemplateDecl(), ToArgTST->getTemplateName().getAsTemplateDecl(), FromQual, ToQual, FromDefault, ToDefault); DiffTemplate(FromArgTST, ToArgTST); } } /// DiffTemplateTemplates - Fills a DiffNode with information about a /// template template difference. void DiffTemplateTemplates(const TSTiterator &FromIter, const TSTiterator &ToIter) { TemplateDecl *FromDecl = GetTemplateDecl(FromIter); TemplateDecl *ToDecl = GetTemplateDecl(ToIter); Tree.SetTemplateTemplateDiff(FromDecl, ToDecl, FromIter.isEnd() && FromDecl, ToIter.isEnd() && ToDecl); Tree.SetSame(FromDecl && ToDecl && FromDecl->getCanonicalDecl() == ToDecl->getCanonicalDecl()); } /// InitializeNonTypeDiffVariables - Helper function for DiffNonTypes static void InitializeNonTypeDiffVariables(ASTContext &Context, const TSTiterator &Iter, NonTypeTemplateParmDecl *Default, llvm::APSInt &Value, bool &HasInt, QualType &IntType, bool &IsNullPtr, Expr *&E, ValueDecl *&VD, bool &NeedAddressOf) { if (!Iter.isEnd()) { switch (Iter->getKind()) { default: llvm_unreachable("unknown ArgumentKind"); case TemplateArgument::Integral: Value = Iter->getAsIntegral(); HasInt = true; IntType = Iter->getIntegralType(); return; case TemplateArgument::Declaration: { VD = Iter->getAsDecl(); QualType ArgType = Iter->getParamTypeForDecl(); QualType VDType = VD->getType(); if (ArgType->isPointerType() && Context.hasSameType(ArgType->getPointeeType(), VDType)) NeedAddressOf = true; return; } case TemplateArgument::NullPtr: IsNullPtr = true; return; case TemplateArgument::Expression: E = Iter->getAsExpr(); } } else if (!Default->isParameterPack()) { E = Default->getDefaultArgument(); } if (!Iter.hasDesugaredTA()) return; const TemplateArgument& TA = Iter.getDesugaredTA(); switch (TA.getKind()) { default: llvm_unreachable("unknown ArgumentKind"); case TemplateArgument::Integral: Value = TA.getAsIntegral(); HasInt = true; IntType = TA.getIntegralType(); return; case TemplateArgument::Declaration: { VD = TA.getAsDecl(); QualType ArgType = TA.getParamTypeForDecl(); QualType VDType = VD->getType(); if (ArgType->isPointerType() && Context.hasSameType(ArgType->getPointeeType(), VDType)) NeedAddressOf = true; return; } case TemplateArgument::NullPtr: IsNullPtr = true; return; case TemplateArgument::Expression: // TODO: Sometimes, the desugared template argument Expr differs from // the sugared template argument Expr. It may be useful in the future // but for now, it is just discarded. if (!E) E = TA.getAsExpr(); return; } } /// DiffNonTypes - Handles any template parameters not handled by DiffTypes /// of DiffTemplatesTemplates, such as integer and declaration parameters. void DiffNonTypes(const TSTiterator &FromIter, const TSTiterator &ToIter, NonTypeTemplateParmDecl *FromDefaultNonTypeDecl, NonTypeTemplateParmDecl *ToDefaultNonTypeDecl) { Expr *FromExpr = nullptr, *ToExpr = nullptr; llvm::APSInt FromInt, ToInt; QualType FromIntType, ToIntType; ValueDecl *FromValueDecl = nullptr, *ToValueDecl = nullptr; bool HasFromInt = false, HasToInt = false, FromNullPtr = false, ToNullPtr = false, NeedFromAddressOf = false, NeedToAddressOf = false; InitializeNonTypeDiffVariables( Context, FromIter, FromDefaultNonTypeDecl, FromInt, HasFromInt, FromIntType, FromNullPtr, FromExpr, FromValueDecl, NeedFromAddressOf); InitializeNonTypeDiffVariables(Context, ToIter, ToDefaultNonTypeDecl, ToInt, HasToInt, ToIntType, ToNullPtr, ToExpr, ToValueDecl, NeedToAddressOf); bool FromDefault = FromIter.isEnd() && (FromExpr || FromValueDecl || HasFromInt || FromNullPtr); bool ToDefault = ToIter.isEnd() && (ToExpr || ToValueDecl || HasToInt || ToNullPtr); bool FromDeclaration = FromValueDecl || FromNullPtr; bool ToDeclaration = ToValueDecl || ToNullPtr; if (FromDeclaration && HasToInt) { Tree.SetFromDeclarationAndToIntegerDiff( FromValueDecl, NeedFromAddressOf, FromNullPtr, FromExpr, ToInt, HasToInt, ToIntType, ToExpr, FromDefault, ToDefault); Tree.SetSame(false); return; } if (HasFromInt && ToDeclaration) { Tree.SetFromIntegerAndToDeclarationDiff( FromInt, HasFromInt, FromIntType, FromExpr, ToValueDecl, NeedToAddressOf, ToNullPtr, ToExpr, FromDefault, ToDefault); Tree.SetSame(false); return; } if (HasFromInt || HasToInt) { Tree.SetIntegerDiff(FromInt, ToInt, HasFromInt, HasToInt, FromIntType, ToIntType, FromExpr, ToExpr, FromDefault, ToDefault); if (HasFromInt && HasToInt) { Tree.SetSame(Context.hasSameType(FromIntType, ToIntType) && FromInt == ToInt); } return; } if (FromDeclaration || ToDeclaration) { Tree.SetDeclarationDiff(FromValueDecl, ToValueDecl, NeedFromAddressOf, NeedToAddressOf, FromNullPtr, ToNullPtr, FromExpr, ToExpr, FromDefault, ToDefault); bool BothNull = FromNullPtr && ToNullPtr; bool SameValueDecl = FromValueDecl && ToValueDecl && NeedFromAddressOf == NeedToAddressOf && FromValueDecl->getCanonicalDecl() == ToValueDecl->getCanonicalDecl(); Tree.SetSame(BothNull || SameValueDecl); return; } assert((FromExpr || ToExpr) && "Both template arguments cannot be empty."); Tree.SetExpressionDiff(FromExpr, ToExpr, FromDefault, ToDefault); Tree.SetSame(IsEqualExpr(Context, FromExpr, ToExpr)); } /// 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 *ParamsFrom = FromTST->getTemplateName().getAsTemplateDecl()->getTemplateParameters(); TemplateParameterList *ParamsTo = ToTST->getTemplateName().getAsTemplateDecl()->getTemplateParameters(); unsigned TotalArgs = 0; for (TSTiterator FromIter(Context, FromTST), ToIter(Context, 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. unsigned FromParamIndex = std::min(TotalArgs, ParamsFrom->size() - 1); unsigned ToParamIndex = std::min(TotalArgs, ParamsTo->size() - 1); NamedDecl *FromParamND = ParamsFrom->getParam(FromParamIndex); NamedDecl *ToParamND = ParamsTo->getParam(ToParamIndex); assert(FromParamND->getKind() == ToParamND->getKind() && "Parameter Decl are not the same kind."); if (isa(FromParamND)) { DiffTypes(FromIter, ToIter); } else if (isa(FromParamND)) { DiffTemplateTemplates(FromIter, ToIter); } else if (isa(FromParamND)) { NonTypeTemplateParmDecl *FromDefaultNonTypeDecl = cast(FromParamND); NonTypeTemplateParmDecl *ToDefaultNonTypeDecl = cast(ToParamND); DiffNonTypes(FromIter, ToIter, FromDefaultNonTypeDecl, ToDefaultNonTypeDecl); } else { llvm_unreachable("Unexpected Decl type."); } ++FromIter; ++ToIter; Tree.Up(); } } /// makeTemplateList - Dump every template alias into the vector. static void makeTemplateList( SmallVectorImpl &TemplateList, const TemplateSpecializationType *TST) { while (TST) { TemplateList.push_back(TST); if (!TST->isTypeAlias()) return; TST = TST->getAliasedType()->getAs(); } } /// hasSameBaseTemplate - Returns true when the base templates are the same, /// even if the template arguments are not. static bool hasSameBaseTemplate(const TemplateSpecializationType *FromTST, const TemplateSpecializationType *ToTST) { return FromTST->getTemplateName().getAsTemplateDecl()->getCanonicalDecl() == ToTST->getTemplateName().getAsTemplateDecl()->getCanonicalDecl(); } /// 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 (hasSameBaseTemplate(FromTST, ToTST)) return true; // Create vectors of template aliases. SmallVector FromTemplateList, ToTemplateList; makeTemplateList(FromTemplateList, FromTST); makeTemplateList(ToTemplateList, ToTST); SmallVectorImpl::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 (!hasSameBaseTemplate(*FromIter, *ToIter)) 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 (!hasSameBaseTemplate(*FromIter, *ToIter)) break; } FromTST = FromIter[-1]; ToTST = ToIter[-1]; return true; } /// GetType - Retrieves the template type arguments, including default /// arguments. static QualType GetType(const TSTiterator &Iter) { if (!Iter.isEnd()) return Iter->getAsType(); if (Iter.hasDesugaredTA()) return Iter.getDesugaredTA().getAsType(); return QualType(); } /// GetTemplateDecl - Retrieves the template template arguments, including /// default arguments. static TemplateDecl *GetTemplateDecl(const TSTiterator &Iter) { if (!Iter.isEnd()) return Iter->getAsTemplate().getAsTemplateDecl(); if (Iter.hasDesugaredTA()) return Iter.getDesugaredTA().getAsTemplate().getAsTemplateDecl(); return nullptr; } /// IsEqualExpr - Returns true if the expressions are the same in regards to /// template arguments. These expressions are dependent, so profile them /// instead of trying to evaluate them. static bool IsEqualExpr(ASTContext &Context, Expr *FromExpr, Expr *ToExpr) { if (FromExpr == ToExpr) return true; if (!FromExpr || !ToExpr) return false; llvm::FoldingSetNodeID FromID, ToID; FromExpr->Profile(FromID, Context, true); ToExpr->Profile(ToID, Context, true); return FromID == ToID; } // 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'; OS.indent(2 * Indent); ++Indent; } // Handle cases where the difference is not templates with different // arguments. switch (Tree.GetKind()) { case DiffTree::Invalid: llvm_unreachable("Template diffing failed with bad DiffNode"); case DiffTree::Type: { QualType FromType, ToType; Tree.GetTypeDiff(FromType, ToType); PrintTypeNames(FromType, ToType, Tree.FromDefault(), Tree.ToDefault(), Tree.NodeIsSame()); return; } case DiffTree::Expression: { Expr *FromExpr, *ToExpr; Tree.GetExpressionDiff(FromExpr, ToExpr); PrintExpr(FromExpr, ToExpr, Tree.FromDefault(), Tree.ToDefault(), Tree.NodeIsSame()); return; } case DiffTree::TemplateTemplate: { TemplateDecl *FromTD, *ToTD; Tree.GetTemplateTemplateDiff(FromTD, ToTD); PrintTemplateTemplate(FromTD, ToTD, Tree.FromDefault(), Tree.ToDefault(), Tree.NodeIsSame()); return; } case DiffTree::Integer: { llvm::APSInt FromInt, ToInt; Expr *FromExpr, *ToExpr; bool IsValidFromInt, IsValidToInt; QualType FromIntType, ToIntType; Tree.GetIntegerDiff(FromInt, ToInt, IsValidFromInt, IsValidToInt, FromIntType, ToIntType, FromExpr, ToExpr); PrintAPSInt(FromInt, ToInt, IsValidFromInt, IsValidToInt, FromIntType, ToIntType, FromExpr, ToExpr, Tree.FromDefault(), Tree.ToDefault(), Tree.NodeIsSame()); return; } case DiffTree::Declaration: { ValueDecl *FromValueDecl, *ToValueDecl; bool FromAddressOf, ToAddressOf; bool FromNullPtr, ToNullPtr; Expr *FromExpr, *ToExpr; Tree.GetDeclarationDiff(FromValueDecl, ToValueDecl, FromAddressOf, ToAddressOf, FromNullPtr, ToNullPtr, FromExpr, ToExpr); PrintValueDecl(FromValueDecl, ToValueDecl, FromAddressOf, ToAddressOf, FromNullPtr, ToNullPtr, FromExpr, ToExpr, Tree.FromDefault(), Tree.ToDefault(), Tree.NodeIsSame()); return; } case DiffTree::FromDeclarationAndToInteger: { ValueDecl *FromValueDecl; bool FromAddressOf; bool FromNullPtr; Expr *FromExpr; llvm::APSInt ToInt; bool IsValidToInt; QualType ToIntType; Expr *ToExpr; Tree.GetFromDeclarationAndToIntegerDiff( FromValueDecl, FromAddressOf, FromNullPtr, FromExpr, ToInt, IsValidToInt, ToIntType, ToExpr); assert((FromValueDecl || FromNullPtr) && IsValidToInt); PrintValueDeclAndInteger(FromValueDecl, FromAddressOf, FromNullPtr, FromExpr, Tree.FromDefault(), ToInt, ToIntType, ToExpr, Tree.ToDefault()); return; } case DiffTree::FromIntegerAndToDeclaration: { llvm::APSInt FromInt; bool IsValidFromInt; QualType FromIntType; Expr *FromExpr; ValueDecl *ToValueDecl; bool ToAddressOf; bool ToNullPtr; Expr *ToExpr; Tree.GetFromIntegerAndToDeclarationDiff( FromInt, IsValidFromInt, FromIntType, FromExpr, ToValueDecl, ToAddressOf, ToNullPtr, ToExpr); assert(IsValidFromInt && (ToValueDecl || ToNullPtr)); PrintIntegerAndValueDecl(FromInt, FromIntType, FromExpr, Tree.FromDefault(), ToValueDecl, ToAddressOf, ToNullPtr, ToExpr, Tree.ToDefault()); return; } case DiffTree::Template: { // Node is root of template. Recurse on children. TemplateDecl *FromTD, *ToTD; Qualifiers FromQual, ToQual; Tree.GetTemplateDiff(FromTD, ToTD, FromQual, ToQual); PrintQualifiers(FromQual, ToQual); if (!Tree.HasChildren()) { // If we're dealing with a template specialization with zero // arguments, there are no children; special-case this. OS << FromTD->getDeclName() << "<>"; return; } OS << FromTD->getDeclName() << '<'; Tree.MoveToChild(); unsigned NumElideArgs = 0; bool AllArgsElided = true; do { if (ElideType) { if (Tree.NodeIsSame()) { ++NumElideArgs; continue; } AllArgsElided = false; if (NumElideArgs > 0) { PrintElideArgs(NumElideArgs, Indent); NumElideArgs = 0; OS << ", "; } } TreeToString(Indent); if (Tree.HasNextSibling()) OS << ", "; } while (Tree.AdvanceSibling()); if (NumElideArgs > 0) { if (AllArgsElided) OS << "..."; else PrintElideArgs(NumElideArgs, Indent); } Tree.Parent(); OS << ">"; return; } } } // 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(Policy); return; } if (!FromType.isNull() && !ToType.isNull() && FromType.getLocalUnqualifiedType() == ToType.getLocalUnqualifiedType()) { Qualifiers FromQual = FromType.getLocalQualifiers(), ToQual = ToType.getLocalQualifiers(); PrintQualifiers(FromQual, ToQual); FromType.getLocalUnqualifiedType().print(OS, Policy); return; } std::string FromTypeStr = FromType.isNull() ? "(no argument)" : FromType.getAsString(Policy); std::string ToTypeStr = ToType.isNull() ? "(no argument)" : ToType.getAsString(Policy); // Print without ElaboratedType sugar if it is better. // TODO: merge this with other aka printing above. if (FromTypeStr == ToTypeStr) { const auto *FromElTy = dyn_cast(FromType), *ToElTy = dyn_cast(ToType); if (FromElTy || ToElTy) { std::string FromNamedTypeStr = FromElTy ? FromElTy->getNamedType().getAsString(Policy) : FromTypeStr; std::string ToNamedTypeStr = ToElTy ? ToElTy->getNamedType().getAsString(Policy) : ToTypeStr; if (FromNamedTypeStr != ToNamedTypeStr) { FromTypeStr = FromNamedTypeStr; ToTypeStr = ToNamedTypeStr; goto PrintTypes; } } // Switch to canonical typename if it is better. std::string FromCanTypeStr = FromType.getCanonicalType().getAsString(Policy); std::string ToCanTypeStr = ToType.getCanonicalType().getAsString(Policy); if (FromCanTypeStr != ToCanTypeStr) { FromTypeStr = FromCanTypeStr; ToTypeStr = ToCanTypeStr; } } PrintTypes: if (PrintTree) OS << '['; OS << (FromDefault ? "(default) " : ""); Bold(); OS << FromTypeStr; Unbold(); if (PrintTree) { OS << " != " << (ToDefault ? "(default) " : ""); Bold(); OS << ToTypeStr; Unbold(); OS << "]"; } } /// 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) { E->printPretty(OS, nullptr, Policy); return; } OS << "(no argument)"; } /// 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."); std::string FromName = std::string(FromTD ? FromTD->getName() : "(no argument)"); std::string ToName = std::string(ToTD ? ToTD->getName() : "(no argument)"); if (FromTD && ToTD && FromName == ToName) { FromName = FromTD->getQualifiedNameAsString(); ToName = ToTD->getQualifiedNameAsString(); } if (Same) { OS << "template " << FromTD->getDeclName(); } else if (!PrintTree) { OS << (FromDefault ? "(default) template " : "template "); Bold(); OS << FromName; Unbold(); } else { OS << (FromDefault ? "[(default) template " : "[template "); Bold(); OS << FromName; Unbold(); OS << " != " << (ToDefault ? "(default) template " : "template "); Bold(); OS << ToName; Unbold(); OS << ']'; } } /// PrintAPSInt - Handles printing of integral arguments, highlighting /// argument differences. void PrintAPSInt(const llvm::APSInt &FromInt, const llvm::APSInt &ToInt, bool IsValidFromInt, bool IsValidToInt, QualType FromIntType, QualType ToIntType, Expr *FromExpr, Expr *ToExpr, bool FromDefault, bool ToDefault, bool Same) { assert((IsValidFromInt || IsValidToInt) && "Only one integral argument may be missing."); if (Same) { if (FromIntType->isBooleanType()) { OS << ((FromInt == 0) ? "false" : "true"); } else { OS << toString(FromInt, 10); } return; } bool PrintType = IsValidFromInt && IsValidToInt && !Context.hasSameType(FromIntType, ToIntType); if (!PrintTree) { OS << (FromDefault ? "(default) " : ""); PrintAPSInt(FromInt, FromExpr, IsValidFromInt, FromIntType, PrintType); } else { OS << (FromDefault ? "[(default) " : "["); PrintAPSInt(FromInt, FromExpr, IsValidFromInt, FromIntType, PrintType); OS << " != " << (ToDefault ? "(default) " : ""); PrintAPSInt(ToInt, ToExpr, IsValidToInt, ToIntType, PrintType); OS << ']'; } } /// PrintAPSInt - If valid, print the APSInt. If the expression is /// gives more information, print it too. void PrintAPSInt(const llvm::APSInt &Val, Expr *E, bool Valid, QualType IntType, bool PrintType) { Bold(); if (Valid) { if (HasExtraInfo(E)) { PrintExpr(E); Unbold(); OS << " aka "; Bold(); } if (PrintType) { Unbold(); OS << "("; Bold(); IntType.print(OS, Context.getPrintingPolicy()); Unbold(); OS << ") "; Bold(); } if (IntType->isBooleanType()) { OS << ((Val == 0) ? "false" : "true"); } else { OS << toString(Val, 10); } } else if (E) { PrintExpr(E); } else { OS << "(no argument)"; } Unbold(); } /// HasExtraInfo - Returns true if E is not an integer literal, the /// negation of an integer literal, or a boolean literal. bool HasExtraInfo(Expr *E) { if (!E) return false; E = E->IgnoreImpCasts(); if (isa(E)) return false; if (UnaryOperator *UO = dyn_cast(E)) if (UO->getOpcode() == UO_Minus) if (isa(UO->getSubExpr())) return false; if (isa(E)) return false; return true; } void PrintValueDecl(ValueDecl *VD, bool AddressOf, Expr *E, bool NullPtr) { if (VD) { if (AddressOf) OS << "&"; else if (auto *TPO = dyn_cast(VD)) { // FIXME: Diffing the APValue would be neat. // FIXME: Suppress this and use the full name of the declaration if the // parameter is a pointer or reference. TPO->printAsInit(OS, Policy); return; } VD->printName(OS, Policy); return; } if (NullPtr) { if (E && !isa(E)) { PrintExpr(E); if (IsBold) { Unbold(); OS << " aka "; Bold(); } else { OS << " aka "; } } OS << "nullptr"; return; } OS << "(no argument)"; } /// PrintDecl - Handles printing of Decl arguments, highlighting /// argument differences. void PrintValueDecl(ValueDecl *FromValueDecl, ValueDecl *ToValueDecl, bool FromAddressOf, bool ToAddressOf, bool FromNullPtr, bool ToNullPtr, Expr *FromExpr, Expr *ToExpr, bool FromDefault, bool ToDefault, bool Same) { assert((FromValueDecl || FromNullPtr || ToValueDecl || ToNullPtr) && "Only one Decl argument may be NULL"); if (Same) { PrintValueDecl(FromValueDecl, FromAddressOf, FromExpr, FromNullPtr); } else if (!PrintTree) { OS << (FromDefault ? "(default) " : ""); Bold(); PrintValueDecl(FromValueDecl, FromAddressOf, FromExpr, FromNullPtr); Unbold(); } else { OS << (FromDefault ? "[(default) " : "["); Bold(); PrintValueDecl(FromValueDecl, FromAddressOf, FromExpr, FromNullPtr); Unbold(); OS << " != " << (ToDefault ? "(default) " : ""); Bold(); PrintValueDecl(ToValueDecl, ToAddressOf, ToExpr, ToNullPtr); Unbold(); OS << ']'; } } /// PrintValueDeclAndInteger - Uses the print functions for ValueDecl and /// APSInt to print a mixed difference. void PrintValueDeclAndInteger(ValueDecl *VD, bool NeedAddressOf, bool IsNullPtr, Expr *VDExpr, bool DefaultDecl, const llvm::APSInt &Val, QualType IntType, Expr *IntExpr, bool DefaultInt) { if (!PrintTree) { OS << (DefaultDecl ? "(default) " : ""); Bold(); PrintValueDecl(VD, NeedAddressOf, VDExpr, IsNullPtr); Unbold(); } else { OS << (DefaultDecl ? "[(default) " : "["); Bold(); PrintValueDecl(VD, NeedAddressOf, VDExpr, IsNullPtr); Unbold(); OS << " != " << (DefaultInt ? "(default) " : ""); PrintAPSInt(Val, IntExpr, true /*Valid*/, IntType, false /*PrintType*/); OS << ']'; } } /// PrintIntegerAndValueDecl - Uses the print functions for APSInt and /// ValueDecl to print a mixed difference. void PrintIntegerAndValueDecl(const llvm::APSInt &Val, QualType IntType, Expr *IntExpr, bool DefaultInt, ValueDecl *VD, bool NeedAddressOf, bool IsNullPtr, Expr *VDExpr, bool DefaultDecl) { if (!PrintTree) { OS << (DefaultInt ? "(default) " : ""); PrintAPSInt(Val, IntExpr, true /*Valid*/, IntType, false /*PrintType*/); } else { OS << (DefaultInt ? "[(default) " : "["); PrintAPSInt(Val, IntExpr, true /*Valid*/, IntType, false /*PrintType*/); OS << " != " << (DefaultDecl ? "(default) " : ""); Bold(); PrintValueDecl(VD, NeedAddressOf, VDExpr, IsNullPtr); 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 << " * ...]"; } // Prints and highlights differences in Qualifiers. void PrintQualifiers(Qualifiers FromQual, Qualifiers ToQual) { // Both types have no qualifiers if (FromQual.empty() && ToQual.empty()) return; // Both types have same qualifiers if (FromQual == ToQual) { PrintQualifier(FromQual, /*ApplyBold*/false); return; } // Find common qualifiers and strip them from FromQual and ToQual. Qualifiers CommonQual = Qualifiers::removeCommonQualifiers(FromQual, ToQual); // The qualifiers are printed before the template name. // Inline printing: // The common qualifiers are printed. Then, qualifiers only in this type // are printed and highlighted. Finally, qualifiers only in the other // type are printed and highlighted inside parentheses after "missing". // Tree printing: // Qualifiers are printed next to each other, inside brackets, and // separated by "!=". The printing order is: // common qualifiers, highlighted from qualifiers, "!=", // common qualifiers, highlighted to qualifiers if (PrintTree) { OS << "["; if (CommonQual.empty() && FromQual.empty()) { Bold(); OS << "(no qualifiers) "; Unbold(); } else { PrintQualifier(CommonQual, /*ApplyBold*/false); PrintQualifier(FromQual, /*ApplyBold*/true); } OS << "!= "; if (CommonQual.empty() && ToQual.empty()) { Bold(); OS << "(no qualifiers)"; Unbold(); } else { PrintQualifier(CommonQual, /*ApplyBold*/false, /*appendSpaceIfNonEmpty*/!ToQual.empty()); PrintQualifier(ToQual, /*ApplyBold*/true, /*appendSpaceIfNonEmpty*/false); } OS << "] "; } else { PrintQualifier(CommonQual, /*ApplyBold*/false); PrintQualifier(FromQual, /*ApplyBold*/true); } } void PrintQualifier(Qualifiers Q, bool ApplyBold, bool AppendSpaceIfNonEmpty = true) { if (Q.empty()) return; if (ApplyBold) Bold(); Q.print(OS, Policy, AppendSpaceIfNonEmpty); if (ApplyBold) Unbold(); } public: TemplateDiff(raw_ostream &OS, 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. FromTemplateType(PrintFromType ? FromType : ToType), ToTemplateType(PrintFromType ? ToType : FromType), OS(OS), IsBold(false) { } /// DiffTemplate - Start the template type diffing. void DiffTemplate() { Qualifiers FromQual = FromTemplateType.getQualifiers(), ToQual = ToTemplateType.getQualifiers(); const TemplateSpecializationType *FromOrigTST = GetTemplateSpecializationType(Context, FromTemplateType); const TemplateSpecializationType *ToOrigTST = GetTemplateSpecializationType(Context, ToTemplateType); // Only checking templates. if (!FromOrigTST || !ToOrigTST) return; // Different base templates. if (!hasSameTemplate(FromOrigTST, ToOrigTST)) { return; } FromQual -= QualType(FromOrigTST, 0).getQualifiers(); ToQual -= QualType(ToOrigTST, 0).getQualifiers(); // Same base template, but different arguments. Tree.SetTemplateDiff(FromOrigTST->getTemplateName().getAsTemplateDecl(), ToOrigTST->getTemplateName().getAsTemplateDecl(), FromQual, ToQual, false /*FromDefault*/, false /*ToDefault*/); DiffTemplate(FromOrigTST, ToOrigTST); } /// Emit - When the two types given are templated types with the same /// base template, a string representation of the type difference will be /// emitted to the stream and return true. Otherwise, return false. bool Emit() { Tree.StartTraverse(); if (Tree.Empty()) return false; TreeToString(); assert(!IsBold && "Bold is applied to end of string."); return true; } }; // end class TemplateDiff } // end anonymous 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, raw_ostream &OS) { if (PrintTree) PrintFromType = true; TemplateDiff TD(OS, Context, FromType, ToType, PrintTree, PrintFromType, ElideType, ShowColors); TD.DiffTemplate(); return TD.Emit(); }