//===--- swift-api-digester.cpp - API change detector ---------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // swift-api-digester is a test utility to detect source-breaking API changes // during the evolution of a Swift library. The tool works on two phases: // (1) dumping library contents as a JSON file, and (2) comparing two JSON // files textually to report interesting changes. // // During phase (1), the api-digester looks up every declarations inside // a module and outputs a singly-rooted tree that encloses interesting // details of the API level. // // During phase (2), api-digester applies structure-information comparison // algorithms on two given singly root trees, trying to figure out, as // precise as possible, the branches/leaves in the trees that differ from // each other. Further analysis decides whether the changed leaves/branches // can be reflected as source-breaking changes for API users. If they are, // the output of api-digester will include such changes. #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/Sema.h" #include "llvm/ADT/TinyPtrVector.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/Signals.h" #include "llvm/Support/YAMLParser.h" #include "llvm/Support/YAMLTraits.h" #include "swift/AST/Decl.h" #include "swift/AST/NameLookup.h" #include "swift/AST/PrettyStackTrace.h" #include "swift/AST/USRGeneration.h" #include "swift/Basic/ColorUtils.h" #include "swift/Basic/Fallthrough.h" #include "swift/Basic/JSONSerialization.h" #include "swift/Basic/LLVMInitialize.h" #include "swift/Basic/STLExtras.h" #include "swift/Basic/Version.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/Frontend/Frontend.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" #include "swift/IDE/Utils.h" #include using namespace swift; using namespace ide; namespace { enum class ActionType { None, DumpSDK, DumpSwiftModules, CompareSDKs, DiagnoseSDKs, }; } namespace options { static llvm::cl::opt IncludeAllModules("include-all", llvm::cl::desc("Include all modules from the SDK")); static llvm::cl::list ModuleNames("module", llvm::cl::ZeroOrMore, llvm::cl::desc("Names of modules")); static llvm::cl::opt ModuleList("module-list-file", llvm::cl::desc("File containing a new-line separated list of modules")); static llvm::cl::opt OutputFile("o", llvm::cl::desc("Output file")); static llvm::cl::opt SDK("sdk", llvm::cl::desc("path to the SDK to build against")); static llvm::cl::opt Triple("target", llvm::cl::desc("target triple")); static llvm::cl::opt ModuleCachePath("module-cache-path", llvm::cl::desc("Clang module cache path")); static llvm::cl::opt ResourceDir("resource-dir", llvm::cl::desc("The directory that holds the compiler resource files")); static llvm::cl::list FrameworkPaths("F", llvm::cl::desc("add a directory to the framework search path")); static llvm::cl::list ModuleInputPaths("I", llvm::cl::desc("add a module for input")); static llvm::cl::list CCSystemFrameworkPaths("iframework", llvm::cl::desc("add a directory to the clang importer system framework search path")); static llvm::cl::opt AbortOnModuleLoadFailure("abort-on-module-fail", llvm::cl::desc("Abort if a module failed to load")); static llvm::cl::opt Verbose("v", llvm::cl::desc("Verbose")); static llvm::cl::opt Action(llvm::cl::desc("Mode:"), llvm::cl::init(ActionType::None), llvm::cl::values( clEnumValN(ActionType::DumpSDK, "dump-sdk", "Dump SDK content to JSON file"), clEnumValN(ActionType::DumpSwiftModules, "dump-swift", "dump swift modules in SDK"), clEnumValN(ActionType::CompareSDKs, "compare-sdk", "Compare SDK content in JSON file"), clEnumValN(ActionType::DiagnoseSDKs, "diagnose-sdk", "Diagnose SDK content in JSON file"), clEnumValEnd)); static llvm::cl::list SDKJsonPaths("input-paths", llvm::cl::desc("The SDK contents under comparison")); static llvm::cl::list ApisPrintUsrs("api-usrs", llvm::cl::desc("The name of APIs to print their usrs, " "e.g. Type::Function")); static llvm::cl::opt IgnoreRemovedDeclUSRs("ignored-usrs", llvm::cl::desc("the file containing USRs of removed decls " "that the digester should ignore")); static llvm::cl::opt SwiftVersion("swift-version", llvm::cl::desc("The Swift compiler version to invoke")); } namespace { StringRef InsertToBuffer(StringRef S) { static llvm::StringSet<> Data; return Data.insert(S).first->getKey(); } template bool contains(std::vector container, T instance) { return std::find(container.begin(), container.end(), instance) != container.end(); } template bool contains(ArrayRef container, T instance) { return std::find(container.begin(), container.end(), instance) != container.end(); } class SDKNode; typedef SDKNode* NodePtr; typedef std::unique_ptr NodeUniquePtr; typedef std::map ParentMap; typedef std::vector NodeOwnedVector; typedef std::vector NodeVector; // The interface used to visit the SDK tree. class SDKNodeVisitor { friend SDKNode; protected: NodeVector Ancestors; virtual void visit(NodePtr Node) = 0; NodePtr parent() { if (Ancestors.empty()) return nullptr; return Ancestors.back(); } int depth() { return Ancestors.size() + 1; } public: virtual ~SDKNodeVisitor() = default; }; // During the matching phase, any matched node will be reported using this API. // For update Node left = {Node before change} Right = {Node after change}; // For added Node left = {NilNode} Right = {Node after change}; // For removed Node left = {Node before change} Right = {NilNode} struct MatchedNodeListener { virtual void foundMatch(NodePtr Left, NodePtr Right) = 0; virtual void foundRemoveAddMatch(NodePtr Removed, NodePtr Added) {} virtual ~MatchedNodeListener() = default; }; // A node matcher will traverse two trees of SDKNode and find matched nodes struct NodeMatcher { virtual void match() = 0; virtual ~NodeMatcher() = default; }; #define KEY(NAME) static const char* Key_##NAME = #NAME; #include "DigesterEnums.def" // The node kind appearing in the tree that describes the content of the SDK enum class SDKNodeKind { #define NODE_KIND(NAME) NAME, #include "DigesterEnums.def" }; enum class NodeAnnotation { #define NODE_ANNOTATION(NAME) NAME, #include "DigesterEnums.def" }; enum class KnownTypeKind { #define KNOWN_TYPE(NAME) NAME, #include "DigesterEnums.def" Unknown, }; enum class SDKDeclAttrKind { #define DECL_ATTR(Name) DAK_##Name, #include "DigesterEnums.def" }; // Redefine << so that we can output the name of the node kind. static raw_ostream &operator<<(raw_ostream &Out, const SDKNodeKind Value) { switch (Value) { #define NODE_KIND(Name) case SDKNodeKind::Name: return Out << #Name; #include "DigesterEnums.def" } llvm_unreachable("Undefined SDK node kind."); } // Redefine << so that we can output the name of the annotation kind. static raw_ostream &operator<<(raw_ostream &Out, const NodeAnnotation Value) { #define NODE_ANNOTATION(X) if (Value == NodeAnnotation::X) { return Out << #X; } #include "DigesterEnums.def" llvm_unreachable("Undefined SDK node kind."); } // Redefine << so that we can output the name of decl kind. static raw_ostream &operator<<(raw_ostream &Out, const DeclKind Value) { switch (Value) { #define DECL(X, PARENT) case DeclKind::X: return Out << #X; #include "swift/AST/DeclNodes.def" } } struct SDKNodeInitInfo { StringRef Name; StringRef PrintedName; DeclKind DKind; StringRef USR; StringRef Location; StringRef ModuleName; bool IsThrowing = false; bool IsMutating = false; bool IsStatic = false; Optional SelfIndex; std::vector DeclAttrs; std::vector TypeAttrs; SDKNodeInitInfo() = default; SDKNodeInitInfo(ValueDecl *VD); SDKNodeInitInfo(Type Ty); NodeUniquePtr createSDKNode(SDKNodeKind Kind); }; class SDKNode { typedef std::vector::const_iterator ChildIt; StringRef Name; StringRef PrintedName; unsigned TheKind : 4; NodeOwnedVector Children; std::set Annotations; std::map AnnotateComments; NodePtr Parent = nullptr; protected: SDKNode(SDKNodeInitInfo Info, SDKNodeKind Kind) : Name(Info.Name), PrintedName(Info.PrintedName), TheKind(unsigned(Kind)) {} public: static NodeUniquePtr constructSDKNode(llvm::yaml::MappingNode *Node); static void preorderVisit(NodePtr Root, SDKNodeVisitor &Visitor); static void postorderVisit(NodePtr Root, SDKNodeVisitor &Visitor); bool operator==(const SDKNode &Other) const; bool operator!=(const SDKNode &Other) const { return !((*this) == Other); } bool isLeaf() const { return Children.empty(); } SDKNodeKind getKind() const { return SDKNodeKind(TheKind); } StringRef getName() const { return Name; } bool isNameValid() const { return Name != "_"; } StringRef getPrintedName() const { return PrintedName; } void removeChild(ChildIt CI) { Children.erase(CI); } ChildIt getChildBegin() const { return Children.begin(); } void annotate(NodeAnnotation Anno) { Annotations.insert(Anno); } NodePtr getParent() const { return Parent; }; unsigned getChildrenCount() const { return Children.size(); } NodePtr childAt(unsigned I) const; void removeChild(NodePtr C); void addAnnotateComment(NodeAnnotation Anno, StringRef Comment); StringRef getAnnotateComment(NodeAnnotation Anno) const; bool isAnnotatedAs(NodeAnnotation Anno) const; void addChild(NodeUniquePtr Child); ArrayRef getChildren() const; void collectChildren(NodeVector &Bucket) const; unsigned getChildIndex(NodePtr Child) const; NodePtr getOnlyChild() const; template const T *getAs() const; template T *getAs(); }; class SDKNodeDecl : public SDKNode { DeclKind DKind; StringRef Usr; StringRef Location; StringRef ModuleName; std::vector DeclAttributes; bool IsStatic; bool hasDeclAttribute(SDKDeclAttrKind DAKind) const; protected: SDKNodeDecl(SDKNodeInitInfo Info, SDKNodeKind Kind) : SDKNode(Info, Kind), DKind(Info.DKind), Usr(Info.USR), Location(Info.Location), ModuleName(Info.ModuleName), DeclAttributes(Info.DeclAttrs), IsStatic(Info.IsStatic) {} public: StringRef getUsr() const { return Usr; } StringRef getLocation() const { return Location; } StringRef getModuleName() const {return ModuleName;} void addDeclAttribute(SDKDeclAttrKind DAKind); ArrayRef getDeclAttributes() const; bool isObjc() const { return Usr.startswith("c:"); } static bool classof(const SDKNode *N); DeclKind getDeclKind() const { return DKind; } void printFullyQualifiedName(llvm::raw_ostream &OS); StringRef getFullyQualifiedName(); bool isSDKPrivate(); bool isDeprecated(); bool isStatic() const { return IsStatic; }; }; class SDKNodeType : public SDKNode { std::vector TypeAttributes; protected: bool hasTypeAttribute(TypeAttrKind DAKind) const; SDKNodeType(SDKNodeInitInfo Info, SDKNodeKind Kind) : SDKNode(Info, Kind), TypeAttributes(Info.TypeAttrs) {} public: KnownTypeKind getTypeKind() const; void addTypeAttribute(TypeAttrKind AttrKind); ArrayRef getTypeAttributes() const; SDKNodeDecl *getClosestParentDecl() const; static bool classof(const SDKNode *N); }; bool SDKNodeType::classof(const SDKNode *N) { switch (N->getKind()) { case SDKNodeKind::TypeNominal: case SDKNodeKind::TypeFunc: return true; default: return false; } } class SDKNodeTypeNominal : public SDKNodeType { public: SDKNodeTypeNominal(SDKNodeInitInfo Info) : SDKNodeType(Info, SDKNodeKind::TypeNominal) {} static bool classof(const SDKNode *N); }; class SDKNodeTypeFunc : public SDKNodeType { public: SDKNodeTypeFunc(SDKNodeInitInfo Info) : SDKNodeType(Info, SDKNodeKind::TypeFunc) {} bool isEscaping() const { return !hasTypeAttribute(TypeAttrKind::TAK_noescape); } static bool classof(const SDKNode *N); }; template const T * SDKNode::getAs() const { if (T::classof(this)) return static_cast(this); return nullptr; } template T * SDKNode::getAs() { if (T::classof(this)) return static_cast(this); return nullptr; } unsigned SDKNode::getChildIndex(NodePtr Child) const { return std::find_if(Children.begin(), Children.end(), [&](const NodeUniquePtr &P) { return P.get() == Child; }) - Children.begin(); } NodePtr SDKNode::getOnlyChild() const { assert(Children.size() == 1 && "more that one child."); return (*Children.begin()).get(); } void SDKNode::addChild(NodeUniquePtr Child) { Child->Parent = this; Children.push_back(std::move(Child)); } ArrayRef SDKNode::getChildren() const { return llvm::makeArrayRef(Children.data(), Children.size()); } void SDKNode::collectChildren(NodeVector &Bucket) const { for (auto &C : getChildren()) Bucket.push_back(C.get()); } NodePtr SDKNode::childAt(unsigned I) const { assert(I < getChildrenCount()); return getChildren()[I].get(); } void SDKNode::removeChild(NodePtr C) { Children.erase(std::find_if(Children.begin(), Children.end(), [&](NodeUniquePtr &N) { return N.get() == C; })); } void SDKNode::addAnnotateComment(NodeAnnotation Anno, StringRef Comment) { assert(isAnnotatedAs(Anno) && "Cannot find annotation"); AnnotateComments[Anno] = Comment; } StringRef SDKNode::getAnnotateComment(NodeAnnotation Anno) const { return AnnotateComments.find(Anno)->second; } bool SDKNode::isAnnotatedAs(NodeAnnotation Anno) const { return Annotations.find(Anno) != Annotations.end();; } void SDKNode::preorderVisit(NodePtr Root, SDKNodeVisitor &Visitor) { Visitor.visit(Root); Visitor.Ancestors.push_back(Root); for (auto &Child : Root->Children) preorderVisit(Child.get(), Visitor); Visitor.Ancestors.pop_back(); } void SDKNode::postorderVisit(NodePtr Root, SDKNodeVisitor &Visitor) { Visitor.Ancestors.push_back(Root); for (auto &Child : Root->Children) postorderVisit(Child.get(), Visitor); Visitor.Ancestors.pop_back(); Visitor.visit(Root); } class SDKNodeVectorViewer { ArrayRef Collection; llvm::function_ref Selector; typedef ArrayRef::const_iterator VectorIt; VectorIt getNext(VectorIt Start); class ViewerIterator; public: SDKNodeVectorViewer(ArrayRef Collection, llvm::function_ref Selector) : Collection(Collection), Selector(Selector) {} ViewerIterator begin(); ViewerIterator end(); }; class SDKNodeVectorViewer::ViewerIterator : public std::iterator { SDKNodeVectorViewer &Viewer; VectorIt P; public: ViewerIterator(SDKNodeVectorViewer &Viewer, VectorIt P) : Viewer(Viewer), P(P) {} ViewerIterator(const ViewerIterator& mit) : Viewer(mit.Viewer), P(mit.P) {} ViewerIterator& operator++(); ViewerIterator operator++(int) {ViewerIterator tmp(*this); operator++(); return tmp;} bool operator==(const ViewerIterator& rhs) {return P==rhs.P;} bool operator!=(const ViewerIterator& rhs) {return P!=rhs.P;} const NodePtr& operator*() {return *P;} }; SDKNodeVectorViewer::VectorIt SDKNodeVectorViewer::getNext(VectorIt Start) { for (auto It = Start; It != Collection.end(); ++ It) if (Selector(*It)) return It; return Collection.end(); } SDKNodeVectorViewer::ViewerIterator& SDKNodeVectorViewer::ViewerIterator::operator++() { P = Viewer.getNext(P + 1); return *this; } SDKNodeVectorViewer::ViewerIterator SDKNodeVectorViewer::begin() { return ViewerIterator(*this, getNext(Collection.begin())); } SDKNodeVectorViewer::ViewerIterator SDKNodeVectorViewer::end() { return ViewerIterator(*this, Collection.end()); } class SDKNodeDecl; KnownTypeKind SDKNodeType::getTypeKind() const { #define KNOWN_TYPE(NAME) if (getName() == #NAME) return KnownTypeKind::NAME; #include "DigesterEnums.def" return KnownTypeKind::Unknown; } ArrayRef SDKNodeType::getTypeAttributes() const { return llvm::makeArrayRef(TypeAttributes.data(), TypeAttributes.size()); } void SDKNodeType::addTypeAttribute(TypeAttrKind AttrKind) { TypeAttributes.push_back(AttrKind); } bool SDKNodeType::hasTypeAttribute(TypeAttrKind DAKind) const { return std::find(TypeAttributes.begin(), TypeAttributes.end(), DAKind) != TypeAttributes.end(); } class SDKNodeRoot : public SDKNode { public: SDKNodeRoot(SDKNodeInitInfo Info) : SDKNode(Info, SDKNodeKind::Root) {} static NodeUniquePtr getInstance(); static bool classof(const SDKNode *N); }; NodeUniquePtr SDKNodeRoot::getInstance() { SDKNodeInitInfo Info; Info.Name = InsertToBuffer("TopLevel"); Info.PrintedName = InsertToBuffer("TopLevel"); return Info.createSDKNode(SDKNodeKind::Root); } class SDKNodeNil : public SDKNode { friend struct SDKNodeInitInfo; SDKNodeNil(SDKNodeInitInfo Info) : SDKNode(Info, SDKNodeKind::Nil) {} public: static SDKNode *getInstance(); static bool classof(const SDKNode *N); }; SDKNode* SDKNodeNil::getInstance() { static NodeUniquePtr Instance(new SDKNodeNil(SDKNodeInitInfo())); return Instance.get(); } bool SDKNodeDecl::isDeprecated() { return hasDeclAttribute(SDKDeclAttrKind::DAK_deprecated); } bool SDKNodeDecl::isSDKPrivate() { if (getName().startswith("__")) return true; if (auto *PD = dyn_cast(getParent())) return PD->isSDKPrivate(); return false; } void SDKNodeDecl::printFullyQualifiedName(llvm::raw_ostream &OS) { std::vector Parent; for (auto *P = getParent(); isa(P); P = P->getParent()) Parent.push_back(P); for (auto It = Parent.rbegin(); It != Parent.rend(); ++ It) OS << (*It)->getPrintedName() << "."; OS << getPrintedName(); } StringRef SDKNodeDecl::getFullyQualifiedName() { llvm::SmallString<32> Buffer; llvm::raw_svector_ostream OS(Buffer); printFullyQualifiedName(OS); return InsertToBuffer(OS.str()); } bool SDKNodeDecl::classof(const SDKNode *N) { switch (N->getKind()) { case SDKNodeKind::Constructor: case SDKNodeKind::Function: case SDKNodeKind::Getter: case SDKNodeKind::Setter: case SDKNodeKind::TypeAlias: case SDKNodeKind::TypeDecl: case SDKNodeKind::Var: return true; case SDKNodeKind::Nil: case SDKNodeKind::Root: case SDKNodeKind::TypeNominal: case SDKNodeKind::TypeFunc: return false; } } void SDKNodeDecl::addDeclAttribute(SDKDeclAttrKind DAKind) { DeclAttributes.push_back(DAKind); } bool SDKNodeDecl::hasDeclAttribute(SDKDeclAttrKind DAKind) const { return std::find(DeclAttributes.begin(), DeclAttributes.end(), DAKind) != DeclAttributes.end(); } ArrayRef SDKNodeDecl::getDeclAttributes() const { return llvm::makeArrayRef(DeclAttributes.data(), DeclAttributes.size()); } SDKNodeDecl *SDKNodeType::getClosestParentDecl() const { auto *Result = getParent(); for (; !isa(Result); Result = Result->getParent()); return Result->getAs(); } class SDKNodeTypeDecl : public SDKNodeDecl { public: SDKNodeTypeDecl(SDKNodeInitInfo Info) : SDKNodeDecl(Info, SDKNodeKind::TypeDecl) {} static bool classof(const SDKNode *N); }; class SDKNodeTypeAlias : public SDKNodeDecl { public: SDKNodeTypeAlias(SDKNodeInitInfo Info) : SDKNodeDecl(Info, SDKNodeKind::TypeAlias) {} static bool classof(const SDKNode *N); }; class SDKNodeVar : public SDKNodeDecl { public: SDKNodeVar(SDKNodeInitInfo Info) : SDKNodeDecl(Info, SDKNodeKind::Var) {} static bool classof(const SDKNode *N); }; class SDKNodeAbstractFunc : public SDKNodeDecl { const bool IsThrowing; const bool IsMutating; const Optional SelfIndex; protected: SDKNodeAbstractFunc(SDKNodeInitInfo Info, SDKNodeKind Kind) : SDKNodeDecl(Info, Kind), IsThrowing(Info.IsThrowing), IsMutating(Info.IsMutating), SelfIndex(Info.SelfIndex){} public: bool isThrowing() const { return IsThrowing; } bool isMutating() const { return IsMutating; } uint8_t getSelfIndex() const { return SelfIndex.getValue(); } Optional getSelfIndexOptional() const { return SelfIndex; } bool hasSelfIndex() const { return SelfIndex.hasValue(); } static bool classof(const SDKNode *N); static StringRef getTypeRoleDescription(unsigned Index); }; bool SDKNodeAbstractFunc::classof(const SDKNode *N) { switch (N->getKind()) { case SDKNodeKind::Function: case SDKNodeKind::Setter: case SDKNodeKind::Getter: case SDKNodeKind::Constructor: return true; default: return false; } } class SDKNodeFunction : public SDKNodeAbstractFunc { public: SDKNodeFunction(SDKNodeInitInfo Info) : SDKNodeAbstractFunc(Info, SDKNodeKind::Function) {} SDKNode *getReturnType() { return (*getChildBegin()).get(); } static bool classof(const SDKNode *N); }; StringRef SDKNodeAbstractFunc::getTypeRoleDescription(unsigned Index) { if (Index == 0) { return InsertToBuffer("return"); } else if (Index == 1) { return InsertToBuffer("1st parameter"); } else if (Index == 2) { return InsertToBuffer("2nd parameter"); } else if (Index == 3) { return InsertToBuffer("3rd parameter"); } else { llvm::SmallString<4> Buffer; Buffer += std::to_string(Index); Buffer += "th parameter"; return InsertToBuffer(Buffer.str()); } } class SDKNodeConstructor : public SDKNodeAbstractFunc { public: SDKNodeConstructor(SDKNodeInitInfo Info) : SDKNodeAbstractFunc(Info, SDKNodeKind::Constructor) {} static bool classof(const SDKNode *N); }; class SDKNodeGetter : public SDKNodeAbstractFunc { public: SDKNodeGetter(SDKNodeInitInfo Info) : SDKNodeAbstractFunc(Info, SDKNodeKind::Getter) {} static bool classof(const SDKNode *N); }; class SDKNodeSetter : public SDKNodeAbstractFunc { public: SDKNodeSetter(SDKNodeInitInfo Info) : SDKNodeAbstractFunc(Info, SDKNodeKind::Setter) {} static bool classof(const SDKNode *N); }; #define NODE_KIND(X) \ bool SDKNode##X::classof(const SDKNode *N) { \ return N->getKind() == SDKNodeKind::X; \ } #include "DigesterEnums.def" NodeUniquePtr SDKNode::constructSDKNode(llvm::yaml::MappingNode *Node) { static auto GetScalarString = [&](llvm::yaml::Node *N) -> StringRef { auto WithQuote = cast(N)->getRawValue(); return WithQuote.substr(1, WithQuote.size() - 2); }; SDKNodeKind Kind; SDKNodeInitInfo Info; NodeOwnedVector Children; for (auto Pair : *Node) { auto Key = GetScalarString(Pair.getKey()); if (Key == Key_kind) { Kind = llvm::StringSwitch(GetScalarString(Pair.getValue())) #define NODE_KIND(NAME) .Case(#NAME, SDKNodeKind::NAME) #include "DigesterEnums.def" ; } else if (Key == Key_name) { Info.Name = GetScalarString(Pair.getValue()); } else if (Key == Key_selfIndex) { Info.SelfIndex = std::stoi(cast(Pair.getValue())-> getRawValue()); } else if (Key == Key_usr) { Info.USR = GetScalarString(Pair.getValue()); } else if (Key == Key_location) { Info.Location = GetScalarString(Pair.getValue()); } else if (Key == Key_children) { for (auto &Mapping : *cast(Pair.getValue())) { Children.push_back(constructSDKNode(cast(&Mapping))); } } else if (Key == Key_printedName) { Info.PrintedName = GetScalarString(Pair.getValue()); } else if (Key == Key_moduleName) { Info.ModuleName = GetScalarString(Pair.getValue()); } else if (Key == Key_throwing) { Info.IsThrowing = true; } else if (Key == Key_mutating) { Info.IsMutating = true; } else if (Key == Key_static) { Info.IsStatic = true; } else if (Key == Key_typeAttributes) { auto *Seq = cast(Pair.getValue()); for (auto It = Seq->begin(); It != Seq->end(); ++ It) { Info.TypeAttrs.push_back( llvm::StringSwitch(GetScalarString(&*It)) #define TYPE_ATTR(X) .Case(#X, TypeAttrKind::TAK_##X) #include "swift/AST/Attr.def" .Case("Count", TypeAttrKind::TAK_Count)); } } else if (Key == Key_declAttributes) { auto *Seq = cast(Pair.getValue()); for (auto It = Seq->begin(); It != Seq->end(); ++ It) { Info.DeclAttrs.push_back( llvm::StringSwitch(GetScalarString(&*It)) #define DECL_ATTR(X) .Case(#X, SDKDeclAttrKind::DAK_##X) #include "DigesterEnums.def" ); } } else if (Key == Key_declKind) { Info.DKind = llvm::StringSwitch(GetScalarString(Pair.getValue())) #define DECL(X, PARENT) .Case(#X, DeclKind::X) #include "swift/AST/DeclNodes.def" ; } else { llvm_unreachable("Cannot parse key."); } }; NodeUniquePtr Result = Info.createSDKNode(Kind); for (auto &C : Children) { Result->addChild(std::move(C)); } return Result; } bool SDKNode::operator==(const SDKNode &Other) const { if (getKind() != Other.getKind()) return false; switch(getKind()) { case SDKNodeKind::TypeNominal: case SDKNodeKind::TypeFunc: { auto Left = this->getAs(); auto Right = (&Other)->getAs(); return Left->getTypeAttributes().equals(Right->getTypeAttributes()) && Left->getPrintedName() == Right->getPrintedName(); } case SDKNodeKind::Function: case SDKNodeKind::Constructor: case SDKNodeKind::Getter: case SDKNodeKind::Setter: { auto Left = this->getAs(); auto Right = (&Other)->getAs(); if (Left->isMutating() ^ Right->isMutating()) return false; if (Left->isThrowing() ^ Right->isThrowing()) return false; SWIFT_FALLTHROUGH; } case SDKNodeKind::TypeDecl: case SDKNodeKind::Var: case SDKNodeKind::TypeAlias: case SDKNodeKind::Root: case SDKNodeKind::Nil: { if (getPrintedName() == Other.getPrintedName() && Children.size() == Other.Children.size()) { for (unsigned I = 0; I < Children.size(); ++ I) { if (*Children[I] != *Other.Children[I]) return false; } return true; } return false; } } } // The pretty printer of a tree of SDKNode class SDKNodeDumpVisitor : public SDKNodeVisitor { void dumpSpace(int Num) { while (Num != 0) { llvm::outs() << "\t"; Num --; } } void visit(NodePtr Node) override { dumpSpace(depth()); llvm::outs() << "[" << Node->getKind() << "]" << Node->getName() << "\n"; } public: SDKNodeDumpVisitor() {}; }; static StringRef getPrintedName(Type Ty) { std::string S; llvm::raw_string_ostream OS(S); PrintOptions PO; PO.SkipAttributes = true; Ty.print(OS, PO); return InsertToBuffer(OS.str()); } static StringRef getTypeName(Type Ty) { if (Ty->getKind() == TypeKind::Paren) { return InsertToBuffer("Paren"); } if (Ty->isVoid()) { return InsertToBuffer("Void"); } if (NameAliasType *NAT = dyn_cast(Ty.getPointer())) { return NAT->getDecl()->getNameStr(); } if (Ty->getAnyNominal()) { return Ty->getAnyNominal()->getNameStr(); } #define TYPE(id, parent) \ if (Ty->getKind() == TypeKind::id) { \ return InsertToBuffer(#id); \ } #include "swift/AST/TypeNodes.def" llvm_unreachable("Unhandled type name."); } static StringRef calculateUsr(ValueDecl *VD) { llvm::SmallString<64> SS; llvm::raw_svector_ostream OS(SS); if (!ide::printDeclUSR(VD, OS)) { return InsertToBuffer(SS.str()); } return StringRef(); } static StringRef calculateLocation(ValueDecl *VD) { auto &Ctx = VD->getASTContext(); auto &Importer = static_cast(*Ctx.getClangModuleLoader()); clang::SourceManager &SM = Importer.getClangPreprocessor().getSourceManager(); if (ClangNode CN = VD->getClangNode()) { clang::SourceLocation Loc = CN.getLocation(); Loc = SM.getFileLoc(Loc); if (Loc.isValid()) return InsertToBuffer(Loc.printToString(SM)); } return StringRef(); } static bool isFunctionTypeNoEscape(Type Ty) { if (auto *AFT = Ty->getAs()) { return AFT->getExtInfo().isNoEscape(); } return false; } static StringRef getPrintedName(ValueDecl *VD) { llvm::SmallString<32> Result; if (auto FD = dyn_cast(VD)) { auto DM = FD->getFullName(); Result.append(DM.getBaseName().empty() ? "_" : DM.getBaseName().str()); Result.append("("); for (auto Arg : DM.getArgumentNames()) { Result.append(Arg.empty() ? "_" : Arg.str()); Result.append(":"); } Result.append(")"); return InsertToBuffer(Result.str()); } auto DM = VD->getFullName(); Result.append(DM.getBaseName().str()); return InsertToBuffer(Result.str()); } static bool isFuncThrowing(ValueDecl *VD) { if (auto AF = dyn_cast(VD)) { return AF->hasThrows(); } return false; } static bool isFuncMutating(ValueDecl *VD) { if (auto AF = dyn_cast(VD)) { return AF->isMutating(); } return false; } static Optional getSelfIndex(ValueDecl *VD) { if (auto AF = dyn_cast(VD)) { if (AF->isImportAsInstanceMember()) return AF->getSelfIndex(); } return None; } SDKNodeInitInfo::SDKNodeInitInfo(Type Ty) : Name(getTypeName(Ty)), PrintedName(getPrintedName(Ty)) { if (isFunctionTypeNoEscape(Ty)) TypeAttrs.push_back(TypeAttrKind::TAK_noescape); } SDKNodeInitInfo::SDKNodeInitInfo(ValueDecl *VD) : Name(VD->hasName() ? VD->getName().str() : InsertToBuffer("_")), PrintedName(getPrintedName(VD)), DKind(VD->getKind()), USR(calculateUsr(VD)), Location(calculateLocation(VD)), ModuleName(VD->getModuleContext()->getName().str()), IsThrowing(isFuncThrowing(VD)), IsMutating(isFuncMutating(VD)), IsStatic(VD->isStatic()), SelfIndex(getSelfIndex(VD)) { if (VD->getAttrs().getDeprecated(VD->getASTContext())) DeclAttrs.push_back(SDKDeclAttrKind::DAK_deprecated); } NodeUniquePtr SDKNodeInitInfo::createSDKNode(SDKNodeKind Kind) { NodeUniquePtr Result; switch(Kind) { #define NODE_KIND(X) \ case SDKNodeKind::X: \ Result.reset(static_cast(new SDKNode##X(*this))); \ break; #include "DigesterEnums.def" } return Result; } // Recursively construct a node that represents a type, for instance, // representing the return value type of a function decl. static NodeUniquePtr constructTypeNode(Type T) { NodeUniquePtr Root = SDKNodeInitInfo(T).createSDKNode(SDKNodeKind::TypeNominal); if (isa(T.getPointer())) return Root; if (auto Fun = T->getAs()) { NodeUniquePtr Root = SDKNodeInitInfo(T).createSDKNode(SDKNodeKind::TypeFunc); // Still, return type first Root->addChild(constructTypeNode(Fun->getResult())); Root->addChild(constructTypeNode(Fun->getInput())); return Root; } // Keep paren type as a stand-alone level. if (auto *PT = dyn_cast(T.getPointer())) { Root->addChild(constructTypeNode(PT->getSinglyDesugaredType())); return Root; } // Handle the case where Type has sub-types. if (auto BGT = T->getAs()) { for (auto Arg : BGT->getGenericArgs()) { Root->addChild(constructTypeNode(Arg)); } } else if (auto Tup = T->getAs()) { for (auto Elt : Tup->getElementTypes()) Root->addChild(constructTypeNode(Elt)); } else if (auto MTT = T->getAs()) { Root->addChild(constructTypeNode(MTT->getInstanceType())); } else if (auto ATT = T->getAs()) { for (auto Pro : ATT->getConformsTo()) { Root->addChild(constructTypeNode(Pro->getDeclaredType())); } } return Root; } // Construct a node for a function decl. The first child of the function decl // is guaranteed to be the return value type of this function. // We sometimes skip the first parameter because it can be metatype of dynamic // this if the function is a member function. static NodeUniquePtr constructFunctionNode(FuncDecl* FD, SDKNodeKind Kind, bool SkipFirst) { auto Func = SDKNodeInitInfo(FD).createSDKNode(Kind); Func->addChild(constructTypeNode(FD->getResultType())); for (auto *paramList : FD->getParameterLists()) { for (auto param : *paramList) Func->addChild(constructTypeNode(param->getInterfaceType())); } if (SkipFirst) { Func->removeChild(Func->getChildBegin() + 1); } return Func; } static NodeUniquePtr constructInitNode(ConstructorDecl *CD) { auto Func = SDKNodeInitInfo(CD).createSDKNode(SDKNodeKind::Constructor); Func->addChild(constructTypeNode(CD->getResultType())); for (auto *paramList : CD->getParameterLists()) { for (auto param : *paramList) Func->addChild(constructTypeNode(param->getInterfaceType())); } // Always remove the first parameter in init. Func->removeChild(Func->getChildBegin() + 1); return Func; } static bool shouldIgnore(Decl *D) { if (D->isPrivateStdlibDecl(false)) return true; if (AvailableAttr::isUnavailable(D)) return true; if (isa(D)) return false; if (isa(D)) return true; if (auto VD = dyn_cast(D)) { if (VD->isOperator()) return true; if (VD->getName().empty()) return true; switch (VD->getFormalAccess()) { case Accessibility::Internal: case Accessibility::Private: case Accessibility::FilePrivate: return true; case Accessibility::Public: case Accessibility::Open: break; } } if (auto *ClangD = D->getClangDecl()) { if (isa(ClangD)) return true; if (isa(ClangD)) return true; if (ClangD->hasAttr()) return true; } return false; } static void addMembersToRoot(NodeUniquePtr &Root, IterableDeclContext *Context); static NodeUniquePtr constructTypeDeclNode(NominalTypeDecl *NTD) { auto TypeNode = SDKNodeInitInfo(NTD).createSDKNode(SDKNodeKind::TypeDecl); addMembersToRoot(TypeNode, NTD); for (auto Ext : NTD->getExtensions()) { addMembersToRoot(TypeNode, Ext); } return TypeNode; } static NodeUniquePtr constructVarNode(ValueDecl *VD) { auto Var = SDKNodeInitInfo(VD).createSDKNode(SDKNodeKind::Var); Var->addChild(constructTypeNode(VD->getInterfaceType())); if (auto VAD = dyn_cast(VD)) { if (auto Getter = VAD->getGetter()) Var->addChild(constructFunctionNode(Getter, SDKNodeKind::Getter, false)); if (auto Setter = VAD->getSetter()) Var->addChild(constructFunctionNode(Setter, SDKNodeKind::Setter, false)); } return Var; } static NodeUniquePtr constructTypeAliasNode(TypeAliasDecl *TAD) { auto Alias = SDKNodeInitInfo(TAD).createSDKNode(SDKNodeKind::TypeAlias); Alias->addChild(constructTypeNode(TAD->getUnderlyingType())); return Alias; } static void addMembersToRoot(NodeUniquePtr &Root, IterableDeclContext *Context) { for (auto *Member : Context->getMembers()) { if (shouldIgnore(Member)) continue; if (auto Func = dyn_cast(Member)) { Root->addChild(constructFunctionNode(Func, SDKNodeKind::Function, true)); } else if (auto CD = dyn_cast(Member)) { Root->addChild(constructInitNode(CD)); } else if (auto VD = dyn_cast(Member)) { Root->addChild(constructVarNode(VD)); } else if (auto TAD = dyn_cast(Member)) { Root->addChild(constructTypeAliasNode(TAD)); } else if (auto EED = dyn_cast(Member)) { Root->addChild(constructVarNode(EED)); } else if (auto NTD = dyn_cast(Member)) { Root->addChild(constructTypeDeclNode(NTD)); } } } static void emitSDKNodeRoot(raw_ostream&, NodeUniquePtr&); static std::pair, NodeUniquePtr> parseJsonEmit(StringRef); class SwiftDeclCollector : public VisibleDeclConsumer { std::vector> OwnedBuffers; NodeUniquePtr RootNode; llvm::DenseSet KnownDecls; // Collected and sorted after we get all of them. std::vector ClangMacros; public: void visitAllRoots(SDKNodeVisitor &Visitor) { SDKNode::preorderVisit(RootNode.get(), Visitor); } SwiftDeclCollector() : RootNode(SDKNodeRoot::getInstance()) {} // Construct all roots vector from a given file where a forest was // previously dumped. void deSerialize(StringRef Filename) { auto Pair = parseJsonEmit(Filename); OwnedBuffers.push_back(std::move(Pair.first)); RootNode = std::move(Pair.second); } // Serialize the content of all roots to a given file using JSON format. void serialize(StringRef Filename) { std::error_code EC; llvm::raw_fd_ostream fs(Filename, EC, llvm::sys::fs::F_None); emitSDKNodeRoot(fs, RootNode); } // After collecting decls, either from imported modules or from a previously // serialized JSON file, using this function to get the root of the SDK. NodePtr getSDKRoot() { return RootNode.get(); } void printTopLevelNames() { for (auto &Node : RootNode->getChildren()) { llvm::outs() << Node->getKind() << ": " << Node->getName() << '\n'; } } public: void lookupVisibleDecls(ArrayRef Modules) { for (auto M : Modules) { llvm::SmallVector Decls; M->getDisplayDecls(Decls); for (auto D : Decls) { if (auto VD = dyn_cast(D)) foundDecl(VD, DeclVisibilityKind::DynamicLookup); } } // Now sort the macros before processing so that we can have deterministic // output. llvm::array_pod_sort(ClangMacros.begin(), ClangMacros.end(), [](ValueDecl * const *lhs, ValueDecl * const *rhs) -> int { return (*lhs)->getNameStr().compare((*rhs)->getNameStr()); }); for (auto *VD : ClangMacros) processDecl(VD); } void processDecl(ValueDecl *VD) { if (shouldIgnore(VD)) return; if (auto FD = dyn_cast(VD)) { RootNode->addChild(constructFunctionNode(FD, SDKNodeKind::Function, false)); } else if (auto NTD = dyn_cast(VD)) { RootNode->addChild(constructTypeDeclNode(NTD)); } if (auto VAD = dyn_cast(VD)) { RootNode->addChild(constructVarNode(VAD)); } if (auto TAD = dyn_cast(VD)) { RootNode->addChild(constructTypeAliasNode(TAD)); } } void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason) override { if (KnownDecls.count(VD)) return; KnownDecls.insert(VD); if (VD->getClangMacro()) { // Collect macros, we will sort them afterwards. ClangMacros.push_back(VD); return; } processDecl(VD); } }; } // End of anonymous namespace. namespace swift { namespace json { // In the namespace of swift::json, we define several functions so that the // JSON serializer will know how to interpret and dump types defined in this // file. template<> struct ScalarEnumerationTraits { static void enumeration(Output &out, SDKNodeKind &value) { #define NODE_KIND(X) out.enumCase(value, #X, SDKNodeKind::X); #include "DigesterEnums.def" } }; template<> struct ScalarEnumerationTraits { static void enumeration(Output &out, TypeAttrKind &value) { #define TYPE_ATTR(X) out.enumCase(value, #X, TypeAttrKind::TAK_##X); #include "swift/AST/Attr.def" out.enumCase(value, "Count", TypeAttrKind::TAK_Count); } }; template<> struct ScalarEnumerationTraits { static void enumeration(Output &out, SDKDeclAttrKind &value) { #define DECL_ATTR(X) out.enumCase(value, #X, SDKDeclAttrKind::DAK_##X); #include "DigesterEnums.def" } }; template<> struct ScalarEnumerationTraits { static void enumeration(Output &out, DeclKind &value) { #define DECL(X, PARENT) out.enumCase(value, #X, DeclKind::X); #include "swift/AST/DeclNodes.def" } }; template<> struct ObjectTraits { static void mapping(Output &out, NodeUniquePtr &value) { auto Kind = value->getKind(); auto Name = value->getName(); auto PrintedName = value->getPrintedName(); out.mapRequired(Key_kind, Kind); out.mapRequired(Key_name, Name); out.mapRequired(Key_printedName, PrintedName); if (auto D = dyn_cast(value.get())) { DeclKind DK = D->getDeclKind(); StringRef Usr = D->getUsr(); StringRef Location = D->getLocation(); StringRef ModuleName = D->getModuleName(); out.mapRequired(Key_declKind, DK); out.mapRequired(Key_usr, Usr); out.mapRequired(Key_location, Location); out.mapRequired(Key_moduleName, ModuleName); if (auto isStatic = D->isStatic()) out.mapRequired(Key_static, isStatic); if (auto F = dyn_cast(value.get())) { if (bool isThrowing = F->isThrowing()) out.mapRequired(Key_throwing, isThrowing); if (bool isMutating = F->isMutating()) out.mapRequired(Key_mutating, isMutating); if (F->hasSelfIndex()) { auto Index = F->getSelfIndex(); out.mapRequired(Key_selfIndex, Index); } } auto Attributes = D->getDeclAttributes(); if (!Attributes.empty()) out.mapRequired(Key_declAttributes, Attributes); } else if (auto T = dyn_cast(value.get())) { auto Attributes = T->getTypeAttributes(); if (!Attributes.empty()) out.mapRequired(Key_typeAttributes, Attributes); } if (!value->isLeaf()) { ArrayRef Children = value->getChildren(); out.mapRequired(Key_children, Children); } } }; template<> struct ArrayTraits> { static size_t size(Output &out, ArrayRef &seq) { return seq.size(); } static NodeUniquePtr &element(Output &, ArrayRef &seq, size_t index) { return const_cast(seq[index]); } }; template<> struct ArrayTraits> { static size_t size(Output &out, ArrayRef &seq) { return seq.size(); } static TypeAttrKind& element(Output &, ArrayRef &seq, size_t index) { return const_cast(seq[index]); } }; template<> struct ArrayTraits> { static size_t size(Output &out, ArrayRef &seq) { return seq.size(); } static SDKDeclAttrKind& element(Output &, ArrayRef &seq, size_t index) { return const_cast(seq[index]); } }; } } namespace {// Anonymous namespace resumes. // Serialize a forest of SDKNode trees to the given stream. static void emitSDKNodeRoot(llvm::raw_ostream &os, NodeUniquePtr &Root) { json::Output yout(os); yout << Root; } // Deserialize an SDKNode tree. std::pair, NodeUniquePtr> parseJsonEmit(StringRef FileName) { namespace yaml = llvm::yaml; // Load the input file. llvm::ErrorOr> FileBufOrErr = llvm::MemoryBuffer::getFileOrSTDIN(FileName); if (!FileBufOrErr) { llvm_unreachable("Failed to read JSON file"); } StringRef Buffer = FileBufOrErr->get()->getBuffer(); llvm::SourceMgr SM; yaml::Stream Stream(Buffer, SM); NodeUniquePtr Result; for (auto DI = Stream.begin(); DI != Stream.end(); ++ DI) { assert(DI != Stream.end() && "Failed to read a document"); yaml::Node *N = DI->getRoot(); assert(N && "Failed to find a root"); Result = SDKNode::constructSDKNode(cast(N)); } return {std::move(FileBufOrErr.get()), std::move(Result)}; } // Given two NodeVector, this matches SDKNode by the order of their appearance // in the respective NodeVector. We use this in the order-sensitive cases, such // as parameters in a function decl. class SequentialNodeMatcher : public NodeMatcher { ArrayRef Left; ArrayRef Right; MatchedNodeListener &Listener; public: SequentialNodeMatcher(ArrayRef Left, ArrayRef Right, MatchedNodeListener &Listener) : Left(Left), Right(Right), Listener(Listener) {} virtual void match() override { for (unsigned long i = 0; i < std::max(Left.size(), Right.size()); i ++) { auto L = i < Left.size() ? Left[i].get() : SDKNodeNil::getInstance(); auto R = i < Right.size() ? Right[i].get() : SDKNodeNil::getInstance(); if (*L == *R) continue; if (L->getKind() == SDKNodeKind::Nil || R->getKind() == SDKNodeKind::Nil) Listener.foundRemoveAddMatch(L, R); else Listener.foundMatch(L, R); } } }; struct NodeMatch { NodePtr Left; NodePtr Right; }; class BestMatchMatcher : public NodeMatcher { NodeVector &Left; NodeVector &Right; llvm::function_ref CanMatch; llvm::function_ref IsFirstMatchBetter; MatchedNodeListener &Listener; llvm::SmallPtrSet MatchedRight; bool internalCanMatch(NodePtr L, NodePtr R) { return MatchedRight.count(R) == 0 && CanMatch(L, R); } Optional findBestMatch(NodePtr Pin, NodeVector& Candidates) { Optional Best; for (auto Can : Candidates) { if (!internalCanMatch(Pin, Can)) continue; if (!Best.hasValue() || IsFirstMatchBetter({Pin, Can}, {Pin, Best.getValue()})) Best = Can; } return Best; } public: BestMatchMatcher(NodeVector &Left, NodeVector &Right, llvm::function_ref CanMatch, llvm::function_ref IsFirstMatchBetter, MatchedNodeListener &Listener) : Left(Left), Right(Right), CanMatch(CanMatch), IsFirstMatchBetter(IsFirstMatchBetter), Listener(Listener){} virtual void match() { for (auto L : Left) { if (auto Best = findBestMatch(L, Right)) { MatchedRight.insert(Best.getValue()); Listener.foundMatch(L, Best.getValue()); } } } }; class RemovedAddedNodeMatcher : public NodeMatcher, public MatchedNodeListener { NodeVector &Removed; NodeVector &Added; MatchedNodeListener &Listener; NodeVector RemovedMatched; NodeVector AddedMatched; void handleUnmatch(NodeVector &Matched, NodeVector &All, bool Left) { for (auto A : SDKNodeVectorViewer(All, [&](SDKNode *N) { return !contains(Matched, N);})) { if (Left) Listener.foundRemoveAddMatch(A, SDKNodeNil::getInstance()); else Listener.foundRemoveAddMatch(SDKNodeNil::getInstance(), A); } } bool detectFuncToProperty(SDKNode *R, SDKNode *A) { if (R->getKind() == SDKNodeKind::Function) { if (A->getKind() == SDKNodeKind::Var) { if (A->getName().compare_lower(R->getName()) == 0) { R->annotate(NodeAnnotation::GetterToProperty); } else if (R->getName().startswith("get") && R->getName().substr(3).compare_lower(A->getName()) == 0) { R->annotate(NodeAnnotation::GetterToProperty); } else if (R->getName().startswith("set") && R->getName().substr(3).compare_lower(A->getName()) == 0) { R->annotate(NodeAnnotation::SetterToProperty); } else { return false; } foundMatch(R, A); return true; } } return false; } static bool isAnonymousEnum(SDKNodeDecl *N) { return N->getKind() == SDKNodeKind::Var && N->getUsr().startswith("c:@Ea@"); } static bool isNominalEnum(SDKNodeDecl *N) { return N->getKind() == SDKNodeKind::TypeDecl && N->getUsr().startswith("c:@E@"); } static Optional getLastPartOfUsr(SDKNodeDecl *N) { auto LastPartIndex = N->getUsr().find_last_of('@'); if (LastPartIndex == StringRef::npos) return None; return N->getUsr().substr(LastPartIndex + 1); } bool detectModernizeEnum(SDKNodeDecl *R, SDKNodeDecl *A) { if (!isAnonymousEnum(R) || !isNominalEnum(A)) return false; auto LastPartOfR = getLastPartOfUsr(R); if (!LastPartOfR) return false; for (auto &Child : A->getChildren()) { if (auto VC = dyn_cast(Child.get())) { auto LastPartOfA = getLastPartOfUsr(VC); if (LastPartOfA && LastPartOfR.getValue() == LastPartOfA.getValue()) { R->annotate(NodeAnnotation::ModernizeEnum); llvm::Twine FullName = llvm::Twine().concat(A->getName()).concat("."). concat(Child->getName()); R->addAnnotateComment(NodeAnnotation::ModernizeEnum, InsertToBuffer(FullName.str())); foundMatch(R, A); return true; } } } return false; } bool detectSameAnonymousEnum(SDKNodeDecl *R, SDKNodeDecl *A) { if (!isAnonymousEnum(R) || !isAnonymousEnum(A)) return false; auto LastR = getLastPartOfUsr(R); auto LastA = getLastPartOfUsr(A); if (LastR && LastA && LastR.getValue() == LastA.getValue()) { foundMatch(R, A); return true; } return false; } static bool isNameTooSimple(StringRef N) { static std::vector SimpleNames = {"unit", "data", "log", "coding", "url", "name", "date", "datecomponents", "notification", "urlrequest", "personnamecomponents", "measurement", "dateinterval", "indexset"}; return std::find(SimpleNames.begin(), SimpleNames.end(), N) != SimpleNames.end(); } static bool isSimilarName(StringRef L, StringRef R) { auto LL = L.lower(); auto RR = R.lower(); if (isNameTooSimple(LL) || isNameTooSimple(RR)) return false; if (((StringRef)LL).startswith(RR) || ((StringRef)RR).startswith(LL)) return true; if (((StringRef)LL).startswith((llvm::Twine("ns") + RR).str()) || ((StringRef)RR).startswith((llvm::Twine("ns") + LL).str())) return true; if (((StringRef)LL).endswith(RR) || ((StringRef)RR).endswith(LL)) return true; return false; } /// Whether two decls of different decl kinds can be considered as rename. static bool isDeclKindCrossable(DeclKind DK1, DeclKind DK2, bool First) { if (DK1 == DK2) return true; if (DK1 == DeclKind::Var && DK2 == DeclKind::EnumElement) return true; return First && isDeclKindCrossable(DK2, DK1, false); } static bool isRename(NodePtr L, NodePtr R) { if (L->getKind() != R->getKind()) return false; if (isa(L)) return false; if (auto LD = dyn_cast(L)) { auto *RD = R->getAs(); return isDeclKindCrossable(LD->getDeclKind(), RD->getDeclKind(), true) && isSimilarName(LD->getName(), RD->getName()); } return false; } static bool isBetterMatch(NodeMatch Match1, NodeMatch Match2) { assert(Match1.Left == Match2.Left); auto Left = Match1.Left; auto *M1Right = Match1.Right->getAs(); auto *M2Right = Match2.Right->getAs(); // Consider non-deprecated nodes better matches. auto Dep1 = M1Right->isDeprecated(); auto Dep2 = M2Right->isDeprecated(); if (Dep1 ^ Dep2) { return Dep2; } // If two names are identical, measure whose printed names is closer. if (M1Right->getName() == M2Right->getName()) { return M1Right->getPrintedName().edit_distance(Left->getPrintedName()) < M2Right->getPrintedName().edit_distance(Left->getPrintedName()); } #define DIST(A, B) (std::max(A, B) - std::min(A, B)) return DIST(Left->getName().size(), Match1.Right->getName().size()) < DIST(Left->getName().size(), Match2.Right->getName().size()); #undef DIST } void foundMatch(NodePtr R, NodePtr A) override { Listener.foundRemoveAddMatch(R, A); RemovedMatched.push_back(R); AddedMatched.push_back(A); } public: RemovedAddedNodeMatcher(NodeVector &Removed, NodeVector &Added, MatchedNodeListener &Listener) : Removed(Removed), Added(Added), Listener(Listener) {} virtual void match() override { auto IsDecl = [](NodePtr P) { return isa(P); }; for (auto R : SDKNodeVectorViewer(Removed, IsDecl)) { for (auto A : SDKNodeVectorViewer(Added, IsDecl)) { auto RD = R->getAs(); auto AD = A->getAs(); if (detectFuncToProperty(RD, AD) || detectModernizeEnum(RD, AD) || detectSameAnonymousEnum(RD, AD)) { break; } } } // Rename detection starts. NodeVector RenameLeft; NodeVector RenameRight; for (auto Remain : SDKNodeVectorViewer(Removed, [&](SDKNode *N) { return !contains(RemovedMatched, N); })) { RenameLeft.push_back(Remain); } for (auto Remain : SDKNodeVectorViewer(Added, [&](SDKNode *N) { return !contains(AddedMatched, N); })) { RenameRight.push_back(Remain); } BestMatchMatcher RenameMatcher(RenameLeft, RenameRight, isRename, isBetterMatch, *this); RenameMatcher.match(); // Rename detection ends. handleUnmatch(RemovedMatched, Removed, true); handleUnmatch(AddedMatched, Added, false); } }; // Given two NodeVector, this matches SDKNode by the their names; only Nodes with // the identical names will be matched. We use this in name-sensitive but // order-insensitive cases, such as matching types in a module. class SameNameNodeMatcher : public NodeMatcher { NodeVector &Left; NodeVector &Right; MatchedNodeListener &Listener; enum class NameMatchKind { USR, PrintedName, PrintedNameAndUSR, }; // Given two SDK nodes, figure out the reason for why they have the same name. Optional getNameMatchKind(SDKNode *L, SDKNode *R) { if (L->getKind() != R->getKind()) return None; auto *LD = L->getAs(); auto *RD = R->getAs(); assert(LD && RD); auto NameEqual = LD->getPrintedName() == RD->getPrintedName(); auto UsrEqual = LD->getUsr() == RD->getUsr(); if (NameEqual && UsrEqual) return NameMatchKind::PrintedNameAndUSR; else if (NameEqual) return NameMatchKind::PrintedName; else if (UsrEqual) return NameMatchKind::USR; else return None; } struct NameMatchCandidate { SDKNode *Node; NameMatchKind Kind; }; // Get the priority for the favored name match kind. Favored name match kind // locats before less favored ones. ArrayRef getNameMatchKindPriority(SDKNodeKind Kind) { if (Kind == SDKNodeKind::Function) { static NameMatchKind FuncPriority[] = { NameMatchKind::PrintedNameAndUSR, NameMatchKind::USR, NameMatchKind::PrintedName }; return FuncPriority; } else { static NameMatchKind OtherPriority[] = { NameMatchKind::PrintedNameAndUSR, NameMatchKind::PrintedName }; return OtherPriority; } } // Given a list and a priority, find the best matched candidate SDK node. SDKNode* findBestNameMatch(ArrayRef Candidates, ArrayRef Kinds) { for (auto Kind : Kinds) for (auto &Can : Candidates) if (Kind == Can.Kind) return Can.Node; return nullptr; } public: SameNameNodeMatcher(NodeVector &Left, NodeVector &Right, MatchedNodeListener &Listener) : Left(Left), Right(Right), Listener(Listener) {} void match() override ; }; void SameNameNodeMatcher::match() { NodeVector MatchedRight; NodeVector Removed; NodeVector Added; for (auto *LN : Left) { // This collects all the candidates that can match with LN. std::vector Candidates; for (auto *RN : Right) { // If RN has matched before, ignore it. if (contains(MatchedRight, RN)) continue; // If LN and RN have the same name for some reason, keep track of RN. if (auto Kind = getNameMatchKind(LN, RN)) Candidates.push_back({RN, Kind.getValue()}); } // Try to find the best match among all the candidates by the priority name // match kind list. if (auto Match = findBestNameMatch(Candidates, getNameMatchKindPriority(LN->getKind()))) { Listener.foundMatch(LN, Match); MatchedRight.push_back(Match); } else { Removed.push_back(LN); } } for (auto &R : Right) { if (!contains(MatchedRight, R)) { Added.push_back(R); } } RemovedAddedNodeMatcher(Removed, Added, Listener).match(); } // The recursive version of sequential matcher. We do not only match two vectors // of NodePtr but also their descendents. class SequentialRecursiveMatcher : public NodeMatcher { NodePtr &Left; NodePtr &Right; MatchedNodeListener &Listener; void matchInternal(NodePtr L, NodePtr R) { Listener.foundMatch(L, R); if (!L || !R) return; for (unsigned I = 0; I < std::max(L->getChildrenCount(), R->getChildrenCount()); ++ I) { auto Left = I < L->getChildrenCount() ? L->childAt(I) : nullptr; auto Right = I < R->getChildrenCount() ? R->childAt(I): nullptr; matchInternal(Left, Right); } } public: SequentialRecursiveMatcher(NodePtr &Left, NodePtr &Right, MatchedNodeListener &Listener) : Left(Left), Right(Right), Listener(Listener) {} virtual void match() override { matchInternal(Left, Right); } }; // This is the interface of all passes on the given trees rooted at Left and Right. class SDKTreeDiffPass { public: virtual void pass(NodePtr Left, NodePtr Right) = 0; virtual ~SDKTreeDiffPass() {} }; using NodePairVector = std::vector>; // This map keeps track of updated nodes; thus we can conveniently find out what // is the counterpart of a node before or after being updated. class UpdatedNodesMap : public MatchedNodeListener { NodePairVector MapImpl; public: virtual void foundMatch(NodePtr Left, NodePtr Right) { assert(Left->getKind() != SDKNodeKind::Nil && Right->getKind() != SDKNodeKind::Nil && "Not update operation."); MapImpl.push_back(std::make_pair(Left, Right)); } NodePtr findUpdateCounterpart(NodePtr Node) const { assert(Node->isAnnotatedAs(NodeAnnotation::Updated) && "Not update operation."); auto FoundPair = std::find_if(MapImpl.begin(), MapImpl.end(), [&](std::pair Pair) { return Pair.second == Node || Pair.first == Node; }); assert(FoundPair != MapImpl.end() && "Cannot find update counterpart."); return Node == FoundPair->first ? FoundPair->second : FoundPair->first; } }; void detectThrowing(NodePtr L, NodePtr R) { assert(L->getKind() == R->getKind()); if (auto LF = dyn_cast(L)) { auto RF = R->getAs(); if (!LF->isThrowing() && RF->isThrowing()) { LF->annotate(NodeAnnotation::NowThrowing); } } } void detectMutating(NodePtr L, NodePtr R) { assert(L->getKind() == R->getKind()); if (auto LF = dyn_cast(L)) { auto RF = R->getAs(); if (!LF->isMutating() && RF->isMutating()) { LF->annotate(NodeAnnotation::NowMutating); } } } static void detectRename(NodePtr L, NodePtr R) { assert(L->getKind() == R->getKind()); if (isa(L) && L->getPrintedName() != R->getPrintedName()) { L->annotate(NodeAnnotation::Rename); L->annotate(NodeAnnotation::RenameOldName); L->addAnnotateComment(NodeAnnotation::RenameOldName, L->getPrintedName()); L->annotate(NodeAnnotation::RenameNewName); L->addAnnotateComment(NodeAnnotation::RenameNewName, R->getPrintedName()); } } // This is first pass on two given SDKNode trees. This pass removes the common part // of two versions of SDK, leaving only the changed part. class PrunePass : public MatchedNodeListener, public SDKTreeDiffPass { static void removeCommonChildren(NodePtr Left, NodePtr Right) { llvm::SmallPtrSet LeftToRemove; llvm::SmallPtrSet RightToRemove; for (auto &LC : Left->getChildren()) { for (auto &RC : Right->getChildren()) { if (*LC == *RC) { LeftToRemove.insert(LC.get()); RightToRemove.insert(RC.get()); break; } } } for (NodePtr L : LeftToRemove) Left->removeChild(L); for (NodePtr R : RightToRemove) Right->removeChild(R); } std::unique_ptr UpdateMap; public: PrunePass() : UpdateMap(new UpdatedNodesMap()) {} virtual void foundRemoveAddMatch(NodePtr Left, NodePtr Right) override { if (Left->getKind() == SDKNodeKind::Nil) Right->annotate(NodeAnnotation::Added); else if (Right->getKind() == SDKNodeKind::Nil) { Left->annotate(NodeAnnotation::Removed); } else if (Right->getKind() == Left->getKind()) { foundMatch(Left, Right); } else { Left->annotate(NodeAnnotation::Removed); Right->annotate(NodeAnnotation::Added); } } virtual void foundMatch(NodePtr Left, NodePtr Right) override { assert(Left->getKind() == Right->getKind()); SDKNodeKind Kind = Left->getKind(); assert(Left->getKind() != SDKNodeKind::Nil && Right->getKind() != SDKNodeKind::Nil); assert(Kind == SDKNodeKind::Root || *Left != *Right); Left->annotate(NodeAnnotation::Updated); Right->annotate(NodeAnnotation::Updated); // Push the updated node to the map for future reference. UpdateMap->foundMatch(Left, Right); detectRename(Left, Right); detectThrowing(Left, Right); detectMutating(Left, Right); switch(Kind) { case SDKNodeKind::Root: case SDKNodeKind::TypeDecl: { // If the matched nodes are both modules, remove the contained // type decls that are identical. If the matched nodes are both type decls, // remove the contained function decls that are identical. removeCommonChildren(Left, Right); NodeVector LeftChildren; NodeVector RightChildren; Left->collectChildren(LeftChildren); Right->collectChildren(RightChildren); SameNameNodeMatcher(LeftChildren, RightChildren, *this).match(); break; } case SDKNodeKind::Function: case SDKNodeKind::Setter: case SDKNodeKind::Getter: case SDKNodeKind::Constructor: case SDKNodeKind::TypeAlias: case SDKNodeKind::TypeFunc: case SDKNodeKind::TypeNominal: { // If matched nodes are both function/var/TypeAlias decls, mapping their // parameters sequentially. SequentialNodeMatcher(Left->getChildren(), Right->getChildren(), *this).match(); break; } case SDKNodeKind::Var: { auto LC = Left->getChildren()[0].get(); auto RC = Right->getChildren()[0].get(); if (!(*LC == *RC)) foundMatch(LC, RC); break; } case SDKNodeKind::Nil: { llvm_unreachable("nil node cannot be matched."); } } } virtual void pass(NodePtr Left, NodePtr Right) override { foundMatch(Left, Right); } std::unique_ptr getNodeUpdateMap() { return std::move(UpdateMap); } }; // For a given SDK node tree, this will build up a mapping from USR to node using USRToNodeMap = llvm::StringMap; // Class to build up mappings from USR to SDKNode class MapUSRToNode : public SDKNodeVisitor { friend class SDKNode; // for visit() USRToNodeMap usrMap; virtual void visit(NodePtr ptr) override { if (auto D = dyn_cast(ptr)) { usrMap[D->getUsr()] = ptr; } } public: MapUSRToNode() = default; const USRToNodeMap &getMap() const { return usrMap; } void map(NodePtr ptr) { SDKNode::preorderVisit(ptr, *this); } void dump(llvm::raw_ostream &) const; void dump() const { dump(llvm::errs()); } private: MapUSRToNode(MapUSRToNode &) = delete; MapUSRToNode &operator=(MapUSRToNode &) = delete; }; // Class to build up a diff of structurally different nodes, based on the given // USR map for the left (original) side of the diff, based on parent types. class TypeMemberDiffFinder : public SDKNodeVisitor { friend class SDKNode; // for visit() const USRToNodeMap &diffAgainst; // Vector of {givenNodePtr, diffAgainstPtr} NodePairVector TypeMemberDiffs; virtual void visit(NodePtr node) override { // Skip nodes that we don't have a correlate for auto declNode = dyn_cast(node); if (!declNode) return; auto usr = declNode->getUsr(); auto &usrName = usr; if (!diffAgainst.count(usrName)) return; auto diffNode = diffAgainst.lookup(usrName); assert(node && diffNode && "nullptr visited?"); auto nodeParent = node->getParent(); auto diffParent = diffNode->getParent(); assert(nodeParent && diffParent && "trying to check Root?"); if (nodeParent->getKind() == SDKNodeKind::TypeDecl && diffParent->getKind() == SDKNodeKind::Root) TypeMemberDiffs.push_back({diffNode, node}); else if (node->getKind() == SDKNodeKind::Getter && diffNode->getKind() == SDKNodeKind::Function && node->isNameValid()) { diffNode->annotate(NodeAnnotation::Rename); diffNode->annotate(NodeAnnotation::RenameOldName); diffNode->addAnnotateComment(NodeAnnotation::RenameOldName, diffNode->getPrintedName()); diffNode->annotate(NodeAnnotation::RenameNewName); diffNode->addAnnotateComment(NodeAnnotation::RenameNewName, node->getParent()->getPrintedName()); } } public: TypeMemberDiffFinder(const USRToNodeMap &rightUSRMap) : diffAgainst(rightUSRMap) {} void findDiffsFor(NodePtr ptr) { SDKNode::preorderVisit(ptr, *this); } const NodePairVector &getDiffs() const { return TypeMemberDiffs; } void dump(llvm::raw_ostream &) const; void dump() const { dump(llvm::errs()); } private: TypeMemberDiffFinder(const TypeMemberDiffFinder &) = delete; TypeMemberDiffFinder &operator=(const TypeMemberDiffFinder &) = delete; }; // Given a condition, search whether a node satisfies that condition exists // in a tree. class SearchVisitor : public SDKNodeVisitor { bool isFound = false; llvm::function_ref Predicate; public: SearchVisitor(llvm::function_ref Predicate) : Predicate(Predicate) {} virtual void visit(NodePtr Node) override { isFound |= Predicate(Node); } bool search(NodePtr Node) { SDKNode::preorderVisit(Node, *this); return isFound; } }; class ChangeRefinementPass : public SDKTreeDiffPass, public SDKNodeVisitor { bool IsVisitingLeft; std::unique_ptr UpdateMap; #define ANNOTATE(Node, Counter, X, Y) \ auto ToAnnotate = IsVisitingLeft ? Node : Counter; \ ToAnnotate->annotate(IsVisitingLeft ? X : Y); bool detectWrapOptional(SDKNodeType *Node, SDKNodeType *Counter) { if (Node->getTypeKind() != KnownTypeKind::Optional && Node->getTypeKind() != KnownTypeKind::ImplicitlyUnwrappedOptional && Counter->getTypeKind() == KnownTypeKind::Optional && *Node == *Counter->getOnlyChild()) { ANNOTATE(Node, Counter, NodeAnnotation::WrapOptional, NodeAnnotation::UnwrapOptional) return true; } return false; } bool detectWrapImplicitOptional(SDKNodeType *Node, SDKNodeType *Counter) { if (Node->getTypeKind() != KnownTypeKind::Optional && Node->getTypeKind() != KnownTypeKind::ImplicitlyUnwrappedOptional && Counter->getTypeKind() == KnownTypeKind::ImplicitlyUnwrappedOptional && *Node == *Counter->getOnlyChild()) { ANNOTATE(Node, Counter, NodeAnnotation::WrapImplicitOptional, NodeAnnotation::UnwrapOptional) return true; } return false; } bool detectOptionalUpdate(SDKNodeType *Node, SDKNodeType *Counter) { if (Node->getTypeKind() == KnownTypeKind::Optional && Counter->getTypeKind() == KnownTypeKind::ImplicitlyUnwrappedOptional && *Node->getOnlyChild() == *Counter->getOnlyChild()) { ANNOTATE(Node, Counter, NodeAnnotation::OptionalToImplicitOptional, NodeAnnotation::ImplicitOptionalToOptional) return true; } return false; } bool detectUnmanagedUpdate(SDKNodeType *Node, SDKNodeType *Counter) { if (IsVisitingLeft && Node->getTypeKind() == KnownTypeKind::Unmanaged && Counter->getTypeKind() != KnownTypeKind::Unmanaged && *Node->getOnlyChild() == *Counter) { Node->annotate(NodeAnnotation::UnwrapUnmanaged); return true; } return false; } #undef ANNOTATE bool detectTypeRewritten(SDKNodeType *Node, SDKNodeType *Counter) { if (IsVisitingLeft && (Node->getName() != Counter->getName()|| Node->getChildrenCount() != Counter->getChildrenCount())) { Node->annotate(NodeAnnotation::TypeRewritten); Node->annotate(NodeAnnotation::TypeRewrittenLeft); Node->annotate(NodeAnnotation::TypeRewrittenRight); Node->addAnnotateComment(NodeAnnotation::TypeRewrittenLeft, Node->getPrintedName()); Node->addAnnotateComment(NodeAnnotation::TypeRewrittenRight, Counter->getPrintedName()); return true; } return false; } bool isUnhandledCase(SDKNodeType *Node) { auto Counter = UpdateMap->findUpdateCounterpart(Node)->getAs(); return Node->getTypeKind() == KnownTypeKind::Void || Counter->getTypeKind() == KnownTypeKind::Void; } public: ChangeRefinementPass(std::unique_ptr UpdateMap) : UpdateMap(std::move(UpdateMap)) {} virtual void pass(NodePtr Left, NodePtr Right) override { // Post-order visit is necessary since we propagate annotations bottom-up IsVisitingLeft = true; SDKNode::postorderVisit(Left, *this); IsVisitingLeft = false; SDKNode::postorderVisit(Right, *this); } virtual void visit(NodePtr N) override { auto Node = dyn_cast(N); if (!Node || !Node->isAnnotatedAs(NodeAnnotation::Updated) || isUnhandledCase(Node)) return; auto Counter = const_cast(UpdateMap-> findUpdateCounterpart(Node)->getAs()); bool Result = detectWrapOptional(Node, Counter)|| detectOptionalUpdate(Node, Counter)|| detectWrapImplicitOptional(Node, Counter)|| detectUnmanagedUpdate(Node, Counter)|| detectTypeRewritten(Node, Counter); (void) Result; return; } std::unique_ptr getNodeUpdateMap() { return std::move(UpdateMap); } }; // DiffItem describes how an element in SDK evolves in a way that migrator can // read conveniently. Each DiffItem corresponds to one JSON element and contains // sub fields explaining how migrator can assist client code to cope with such // SDK change. For instance, the following first JSON element describes an unwrap // optional change in the first parameter of function "c:@F@CTTextTabGetOptions". // Similarly, the second JSON element describes a type parameter down cast in the // second parameter of function "c:objc(cs)NSXMLDocument(im)insertChildren:atIndex:". // We keep both usrs because in the future this may support auto-rename. class DiffItem { public: SDKNodeKind NodeKind; NodeAnnotation DiffKind; StringRef ChildIndex; StringRef LeftUsr; StringRef RightUsr; StringRef LeftComment; StringRef RightComment; StringRef ModuleName; DiffItem(SDKNodeKind NodeKind, NodeAnnotation DiffKind, StringRef ChildIndex, StringRef LeftUsr, StringRef RightUsr, StringRef LeftComment, StringRef RightComment, StringRef ModuleName) : NodeKind(NodeKind), DiffKind(DiffKind), ChildIndex(ChildIndex), LeftUsr(LeftUsr), RightUsr(RightUsr), LeftComment(LeftComment), RightComment(RightComment), ModuleName(ModuleName) { assert(!ChildIndex.empty() && "Child index is empty."); } bool operator<(DiffItem Other) const { if (auto UsrCompare = LeftUsr.compare(Other.LeftUsr)) return UsrCompare < 0; if (NodeKind != Other.NodeKind) return NodeKind < Other.NodeKind; if (DiffKind != Other.DiffKind) return DiffKind < Other.DiffKind; if (auto ChildCompare = ChildIndex.compare(Other.ChildIndex)) return ChildCompare < 0; return false; } static void describe(llvm::raw_ostream &os) { os << "// SDK_CHANGE(node kind, diff kind, child index, left USR, " "right USR, left comment, right comment)\n"; } static void undef(llvm::raw_ostream &os) { os << "#undef SDK_CHANGE\n"; } void streamDef(llvm::raw_ostream &S) const { S << "SDK_CHANGE(" << NodeKind << ", " << DiffKind << ", \"" << ChildIndex << "\", \"" << LeftUsr << "\", \"" << RightUsr << "\", \"" << LeftComment << "\", \"" << RightComment << "\", \"" << ModuleName << "\")"; } }; typedef std::vector DiffVector; // TypeMemberDiffItem stores info about movements of functions to type members // // Outputs: // // SDK_CHANGE_TYPE_MEMBER(USR, new type context name, new printed name, self // index, old printed name) // // Examples: //----------------------------------------------------------------------------// // Init: // // CGAffineTransformMakeScale(_:_:) // ==> // SDK_CHANGE_TYPE_MEMBER("c:@F@CGAffineTransformMakeScale", // "CGAffineTransform", "init(scaleX:y:)", , // "CGAffineTransformMakeScale(_:_:)") // // Meaning that source should transform like: // let myAffineTransform = CGAffineTransformMakeScale(myX, myY) // ==> // let myAffineTransform = CGAffineTransform(scaleX: myX, y: myY) // // //----------------------------------------------------------------------------// // Static/Class Method: // // CGColorGetConstantColor(_:) // ==> // SDK_CHANGE_TYPE_MEMBER("c:@F@CGColorGetConstantColor", "CGColor", // "constantColor(forName:)", , // "CGColorGetConstantColor(_:)") // // Meaning that source should transform like: // CGColorGetConstantColor(nameOfWhiteColor) // ==> // CGColor.constantColor(forName: nameOfWhiteColor) // // //----------------------------------------------------------------------------// // Instance Method: // // CGEventPost(_:_:) // ==> // SDK_CHANGE_TYPE_MEMBER("c:@F@CGEventPost", "CGEvent", "post(tap:)", 1, // "CGEventPost(_:_:)") // // Meaning that source should transform like: // CGEventPost(myTap, myEvent) // ==> // myEvent.post(tap: myTap) // // //----------------------------------------------------------------------------// // Static/Class Stored Variable: // // kCGColorWhite // ==> // SDK_CHANGE_TYPE_MEMBER("c:@kCGColorWhite", "CGColor", "white", , // "kCGColorWhite") // // Meaning that source should transform like: // let colorName = kCGColorWhite // ==> // let colorName = CGColor.white // // //----------------------------------------------------------------------------// // Instance Computed Property // // // CGColorGetComponents(_:) // ==> // SDK_CHANGE_TYPE_MEMBER("c:@F@CGColorGetComponents", "CGColor", // "components", 0, "CGColorGetComponents(_:)") // // Meaning that source should transform like: // CGColorGetComponents(myColor) // ==> // myColor.components // // struct TypeMemberDiffItem { StringRef usr; StringRef newTypeName; StringRef newPrintedName; Optional selfIndex; StringRef oldPrintedName; static void describe(llvm::raw_ostream &os) { os << "// SDK_CHANGE_TYPE_MEMBER(USR, new typename, new printed name, " "self index, old printed name)\n"; } static void undef(llvm::raw_ostream &os) { os << "#undef SDK_CHANGE_TYPE_MEMBER\n"; } void streamDef(llvm::raw_ostream &os) const { std::string IndexContent = selfIndex.hasValue() ? std::to_string(selfIndex.getValue()) : ""; os << "SDK_CHANGE_TYPE_MEMBER(" << "\"" << usr << "\"" << ", " << "\"" << newTypeName << "\"" << ", " << "\"" << newPrintedName << "\"" << ", " << "\"" << IndexContent << "\"" << ", " << "\"" << oldPrintedName << "\"" << ")"; } bool operator<(TypeMemberDiffItem Other) const { return usr.compare(Other.usr) < 0; } }; typedef std::vector TypeMemberDiffVector; } // End namespace static void printNode(llvm::raw_ostream &os, NodePtr node) { os << "{" << node->getName() << " " << node->getKind() << " " << node->getPrintedName(); if (auto F = dyn_cast(node)) { if (F->hasSelfIndex()) { os << " selfIndex: "; os << F->getSelfIndex(); } } os << "}"; } void MapUSRToNode::dump(llvm::raw_ostream &os) const { for (auto &elt : usrMap) { auto &node = elt.getValue(); os << elt.getKey() << " ==> "; printNode(os, node); if (node->getParent()) { os << " parent: "; printNode(os, node->getParent()); } os << "\n"; } } void TypeMemberDiffFinder::dump(llvm::raw_ostream &os) const { for (auto pair : getDiffs()) { os << " - "; printNode(os, pair.first); os << " parent: "; printNode(os, pair.first->getParent()); os << "\n + "; printNode(os, pair.second); os << " parent: "; printNode(os, pair.second->getParent()); os << "\n\n"; } } namespace { template void removeRedundantAndSort(std::vector &Diffs) { std::set DiffSet(Diffs.begin(), Diffs.end()); Diffs.assign(DiffSet.begin(), DiffSet.end()); std::sort(Diffs.begin(), Diffs.end()); } template void serializeDiffs(llvm::raw_ostream &Fs, std::vector &Diffs) { removeRedundantAndSort(Diffs); Fs << "\n"; T::describe(Fs); for (auto &Diff : Diffs) { Diff.streamDef(Fs); Fs << "\n"; } T::undef(Fs); Fs << "\n"; } static bool isTypeChangeInterestedFuncNode(NodePtr Decl) { switch(Decl->getKind()) { case SDKNodeKind::Constructor: case SDKNodeKind::Function: return true; default: return false; } } static bool isInterested(SDKNodeDecl* Decl, NodeAnnotation Anno) { switch (Anno) { case NodeAnnotation::WrapOptional: case NodeAnnotation::UnwrapOptional: case NodeAnnotation::ImplicitOptionalToOptional: case NodeAnnotation::OptionalToImplicitOptional: case NodeAnnotation::UnwrapUnmanaged: case NodeAnnotation::TypeRewritten: return isTypeChangeInterestedFuncNode(Decl) && Decl->getParent()->getKind() == SDKNodeKind::TypeDecl; default: return true; } } class DiffItemEmitter : public SDKNodeVisitor { DiffVector &AllItems; bool doesAncestorHaveTypeRewritten() { return std::find_if(Ancestors.begin(), Ancestors.end(),[](NodePtr N) { return N->isAnnotatedAs(NodeAnnotation::TypeRewritten); }) != Ancestors.end(); } StringRef getLeftComment(NodePtr Node, NodeAnnotation Anno) { if (Anno == NodeAnnotation::TypeRewritten) return Node->getAnnotateComment(NodeAnnotation::TypeRewrittenLeft); else if (Anno == NodeAnnotation::Rename) return Node->getAnnotateComment(NodeAnnotation::RenameOldName); return StringRef(); } StringRef getRightComment(NodePtr Node, NodeAnnotation Anno) { if (Anno == NodeAnnotation::TypeRewritten) return Node->getAnnotateComment(NodeAnnotation::TypeRewrittenRight); else if (Anno == NodeAnnotation::ModernizeEnum) return Node->getAnnotateComment(NodeAnnotation::ModernizeEnum); else if (Anno == NodeAnnotation::Rename) return Node->getAnnotateComment(NodeAnnotation::RenameNewName); return StringRef(); } bool handleAnnotation(NodePtr Node, SDKNodeDecl *NonTypeParent, StringRef Index, NodeAnnotation Annotation) { if (isInterested(NonTypeParent, Annotation) && Node->isAnnotatedAs(Annotation)) { auto Kind = NonTypeParent->getKind(); StringRef LC = getLeftComment(Node, Annotation); StringRef RC = getRightComment(Node, Annotation); AllItems.emplace_back(Kind, Annotation, Index, NonTypeParent->getUsr(), StringRef(), LC, RC, NonTypeParent->getModuleName()); return true; } return false; } virtual void visit(NodePtr Node) override { SDKNodeDecl *Parent = dyn_cast(Node); if (!Parent) { if (auto TN = dyn_cast(Node)) { Parent = TN->getClosestParentDecl(); } } if (!Parent) return; auto Index = isa(Node) ? getIndexString(Node) : "0"; bool Result = doesAncestorHaveTypeRewritten() || handleAnnotation(Node, Parent, Index, NodeAnnotation::WrapOptional) || handleAnnotation(Node, Parent, Index, NodeAnnotation::UnwrapOptional) || handleAnnotation(Node, Parent, Index, NodeAnnotation::ImplicitOptionalToOptional) || handleAnnotation(Node, Parent, Index, NodeAnnotation::OptionalToImplicitOptional) || handleAnnotation(Node, Parent, Index, NodeAnnotation::UnwrapUnmanaged) || handleAnnotation(Node, Parent, Index, NodeAnnotation::TypeRewritten) || handleAnnotation(Node, Parent, Index, NodeAnnotation::SetterToProperty) || handleAnnotation(Node, Parent, Index, NodeAnnotation::GetterToProperty) || handleAnnotation(Node, Parent, Index, NodeAnnotation::ModernizeEnum) || handleAnnotation(Node, Parent, Index, NodeAnnotation::Rename) || handleAnnotation(Node, Parent, Index, NodeAnnotation::NowThrowing); (void) Result; } StringRef getIndexString(NodePtr Node) { llvm::SmallString<32> Builder; std::vector Indexes; collectIndexes(Node, Indexes); auto First = true; for (auto I : Indexes) { if (!First) Builder.append(":"); else First = false; Builder.append(std::to_string(I)); } return InsertToBuffer(Builder.str()); } void collectIndexes(NodePtr Node, std::vector &Indexes) { for (unsigned I = Ancestors.size(); I > 0 && (I == Ancestors.size() || isa(Ancestors[I])); -- I) { auto Child = I == Ancestors.size() ? Node : Ancestors[I]; auto Parent = Ancestors[I - 1]; Indexes.insert(Indexes.begin(), Parent->getChildIndex(Child)); } } DiffItemEmitter(DiffVector &AllItems) : AllItems(AllItems) {} public: static void collectDiffItems(NodePtr Root, DiffVector &DV) { DiffItemEmitter Emitter(DV); SDKNode::postorderVisit(Root, Emitter); } }; class DiagnosisEmitter : public SDKNodeVisitor { void visitType(SDKNodeType *T); void visitDecl(SDKNodeDecl *D); void visit(NodePtr Node) override; SDKNodeDecl *findAddedDecl(SDKNodeDecl *Node); static StringRef printName(StringRef Name); static StringRef printDiagKeyword(StringRef Name); static void collectAddedDecls(NodePtr Root, std::set &Results); template struct DiagBag { std::vector Diags; ~DiagBag() { llvm::outs() << "\n==================================================== "; T::theme(llvm::outs()); llvm::outs() << " ====================================================\n"; removeRedundantAndSort(Diags); std::for_each(Diags.begin(), Diags.end(), [](T &Diag) { Diag.output(); }); } }; struct RemovedDeclDiag { DeclKind Kind; StringRef Name; bool IsDeprecated; RemovedDeclDiag(DeclKind Kind, StringRef Name, bool IsDeprecated) : Kind(Kind), Name(Name), IsDeprecated(IsDeprecated) {} bool operator<(RemovedDeclDiag Other) const; void output() const; static void theme(raw_ostream &OS) { OS << "Removed Decls"; }; }; struct MovedDeclDiag { DeclKind RemovedKind; DeclKind AddedKind; StringRef RemovedName; StringRef AddedName; MovedDeclDiag(DeclKind RemovedKind, DeclKind AddedKind, StringRef RemovedName, StringRef AddedName) : RemovedKind(RemovedKind), AddedKind(AddedKind), RemovedName(RemovedName), AddedName(AddedName) {} bool operator<(MovedDeclDiag other) const; void output() const; static void theme(raw_ostream &OS) { OS << "Moved Decls"; }; }; struct RenamedDeclDiag { DeclKind KindBefore; DeclKind KindAfter; StringRef NameBefore; StringRef NameAfter; RenamedDeclDiag(DeclKind KindBefore, DeclKind KindAfter, StringRef NameBefore, StringRef NameAfter) : KindBefore(KindBefore), KindAfter(KindAfter), NameBefore(NameBefore), NameAfter(NameAfter) {} bool operator<(RenamedDeclDiag Other) const; void output() const; static void theme(raw_ostream &OS) { OS << "Renamed Decls"; }; }; struct DeclAttrDiag { DeclKind Kind; StringRef DeclName; StringRef AttrName; DeclAttrDiag(DeclKind Kind, StringRef DeclName, StringRef AttrName) : Kind(Kind), DeclName(DeclName), AttrName(AttrName) {} bool operator<(DeclAttrDiag Other) const; void output() const; static void theme(raw_ostream &OS) { OS << "Decl Attribute changes"; }; }; struct DeclTypeChangeDiag { DeclKind Kind; StringRef DeclName; StringRef TypeNameBefore; StringRef TypeNameAfter; StringRef Description; DeclTypeChangeDiag(DeclKind Kind, StringRef DeclName, StringRef TypeNameBefore, StringRef TypeNameAfter, StringRef Description) : Kind(Kind), DeclName(DeclName), TypeNameBefore(TypeNameBefore), TypeNameAfter(TypeNameAfter), Description(Description) {} bool operator<(DeclTypeChangeDiag Other) const; void output() const; static void theme(raw_ostream &OS) { OS << "Type Changes"; }; }; std::set AddedDecls; DiagBag AttrChangedDecls; DiagBag TypeChangedDecls; DiagBag RenamedDecls; DiagBag MovedDecls; DiagBag RemovedDecls; UpdatedNodesMap UpdateMap; DiagnosisEmitter(UpdatedNodesMap &UpdateMap) : UpdateMap(UpdateMap) {} public: static void diagnosis(NodePtr LeftRoot, NodePtr RightRoot, UpdatedNodesMap &UpdateMap); }; void DiagnosisEmitter::collectAddedDecls(NodePtr Root, std::set &Results) { if (auto *D = dyn_cast(Root)) { if (Root->isAnnotatedAs(NodeAnnotation::Added)) Results.insert(D); } for (auto &C : Root->getChildren()) collectAddedDecls(C.get(), Results); } SDKNodeDecl *DiagnosisEmitter::findAddedDecl(SDKNodeDecl *Root) { for (auto *Added : AddedDecls) { if (Root->getKind() == Added->getKind() && Root->getPrintedName() == Added->getPrintedName()) return Added; } return nullptr; } StringRef DiagnosisEmitter::printName(StringRef Name) { OSColor Color(llvm::outs(), llvm::raw_ostream::CYAN); Color << Name; return StringRef(); } StringRef DiagnosisEmitter::printDiagKeyword(StringRef Name) { OSColor Color(llvm::outs(), llvm::raw_ostream::YELLOW); Color << Name; return StringRef(); } bool DiagnosisEmitter::RemovedDeclDiag:: operator<(RemovedDeclDiag Other) const { if (Kind != Other.Kind) return Kind < Other.Kind; return Name.compare(Other.Name) < 0; } void DiagnosisEmitter::RemovedDeclDiag::output() const { llvm::outs() << Kind << " " << printName(Name) << " has been " << printDiagKeyword("removed"); if (IsDeprecated) llvm::outs() << " (deprecated)"; llvm::outs() << "\n"; } bool DiagnosisEmitter::MovedDeclDiag:: operator<(MovedDeclDiag Other) const { if (RemovedKind != Other.RemovedKind) return RemovedKind < Other.RemovedKind; return RemovedName.compare(Other.RemovedName) < 0; } void DiagnosisEmitter::MovedDeclDiag::output() const { llvm::outs() << RemovedKind << " " << printName(RemovedName) << " has been " << printDiagKeyword("moved") << " to " << AddedKind << " " << printName(AddedName) << "\n"; } bool DiagnosisEmitter::RenamedDeclDiag:: operator<(RenamedDeclDiag Other) const { if (KindBefore != Other.KindBefore) return KindBefore < Other.KindBefore; return NameBefore.compare(Other.NameBefore) < 0; } void DiagnosisEmitter::RenamedDeclDiag::output() const { llvm::outs() << KindBefore << " " << printName(NameBefore) << " has been " << printDiagKeyword("renamed") << " to " << KindAfter << " " << printName(NameAfter) << "\n"; } bool DiagnosisEmitter::DeclTypeChangeDiag:: operator<(DeclTypeChangeDiag Other) const { if (Kind != Other.Kind) return Kind < Other.Kind; return DeclName.compare(Other.DeclName) < 0; } void DiagnosisEmitter::DeclTypeChangeDiag::output() const { llvm::outs() << Kind << " " << printName(DeclName) << " has " << Description << " type change from " << printName(TypeNameBefore) << " to " << printName(TypeNameAfter) << "\n"; } bool DiagnosisEmitter::DeclAttrDiag::operator<(DeclAttrDiag Other) const { if (Kind != Other.Kind) return Kind < Other.Kind; return DeclName.compare_lower(Other.DeclName); } void DiagnosisEmitter::DeclAttrDiag::output() const { llvm::outs() << Kind << " " << printName(DeclName) << " is now " << printDiagKeyword(AttrName) << "\n"; } void DiagnosisEmitter::diagnosis(NodePtr LeftRoot, NodePtr RightRoot, UpdatedNodesMap &UpdateMap) { DiagnosisEmitter Emitter(UpdateMap); collectAddedDecls(RightRoot, Emitter.AddedDecls); SDKNode::postorderVisit(LeftRoot, Emitter); } void DiagnosisEmitter::visitDecl(SDKNodeDecl *Node) { if (Node->isSDKPrivate()) return; if (Node->isAnnotatedAs(NodeAnnotation::Removed) && !Node->isAnnotatedAs(NodeAnnotation::Rename)) { if (auto *Added = findAddedDecl(Node)) { MovedDecls.Diags.emplace_back(Node->getDeclKind(), Added->getDeclKind(), Node->getFullyQualifiedName(), Added->getFullyQualifiedName()); } else { RemovedDecls.Diags.emplace_back(Node->getDeclKind(), Node->getFullyQualifiedName(), Node->isDeprecated()); } } if (Node->isAnnotatedAs(NodeAnnotation::Rename)) { auto *Count = UpdateMap.findUpdateCounterpart(Node)->getAs(); RenamedDecls.Diags.emplace_back(Node->getDeclKind(), Count->getDeclKind(), Node->getFullyQualifiedName(), Count->getFullyQualifiedName()); } if (Node->isAnnotatedAs(NodeAnnotation::NowMutating)) { AttrChangedDecls.Diags.emplace_back(Node->getDeclKind(), Node->getFullyQualifiedName(), InsertToBuffer("mutating")); } if (Node->isAnnotatedAs(NodeAnnotation::NowThrowing)) { AttrChangedDecls.Diags.emplace_back(Node->getDeclKind(), Node->getFullyQualifiedName(), InsertToBuffer("throwing")); } } void DiagnosisEmitter::visitType(SDKNodeType *Node) { auto *Parent = Node->getParent()->getAs(); if (!Parent || Parent->isSDKPrivate()) return; if (Node->isAnnotatedAs(NodeAnnotation::Updated)) { auto *Count = UpdateMap.findUpdateCounterpart(Node)->getAs(); StringRef Descriptor; switch (Parent->getKind()) { case SDKNodeKind::Constructor: case SDKNodeKind::Function: case SDKNodeKind::Var: Descriptor = isa(Parent) ? SDKNodeAbstractFunc::getTypeRoleDescription(Parent->getChildIndex(Node)) : InsertToBuffer("declared"); TypeChangedDecls.Diags.emplace_back(Parent->getDeclKind(), Parent->getFullyQualifiedName(), Node->getPrintedName(), Count->getPrintedName(), Descriptor); break; default: break; } } } void DiagnosisEmitter::visit(NodePtr Node) { if (auto *DNode = dyn_cast(Node)) { visitDecl(DNode); } if (auto *TNode = dyn_cast(Node)) { visitType(TNode); } } struct NoEscapeFuncParam { StringRef Usr; unsigned Index; NoEscapeFuncParam(StringRef Usr, unsigned Index) : Usr(Usr), Index(Index) {} static void describe(llvm::raw_ostream &os) { os << "// NOESCAPE_FUNC_PARAM(USR, Index)\n"; } static void undef(llvm::raw_ostream &os) { os << "#undef NOESCAPE_FUNC_PARAM\n"; } void streamDef(llvm::raw_ostream &os) const { os << "NOESCAPE_FUNC_PARAM(" << "\"" << Usr << "\"" << ", " << "\"" << Index << "\"" << ")"; } bool operator<(NoEscapeFuncParam Other) const { if (Usr != Other.Usr) return Usr.compare(Other.Usr) < 0; return Index < Other.Index; } }; typedef std::vector NoEscapeFuncParamVector; class NoEscapingFuncEmitter : public SDKNodeVisitor { NoEscapeFuncParamVector &AllItems; NoEscapingFuncEmitter(NoEscapeFuncParamVector &AllItems) : AllItems(AllItems) {} virtual void visit(NodePtr Node) override { if (Node->getKind() != SDKNodeKind::TypeFunc) return; if (Node->getAs()->isEscaping()) return; auto Parent = Node->getParent(); if (auto ParentFunc = dyn_cast(Parent)) { if (ParentFunc->isObjc()) { unsigned Index = ParentFunc->getChildIndex(Node); AllItems.emplace_back(ParentFunc->getUsr(), Index); } } } public: static void collectDiffItems(NodePtr Root, NoEscapeFuncParamVector &DV) { NoEscapingFuncEmitter Emitter(DV); SDKNode::postorderVisit(Root, Emitter); } }; /// This info is about functions meet the following criteria: /// - This function is a member function of a type. /// - This function is overloaded. struct OverloadedFuncInfo { StringRef Usr; OverloadedFuncInfo(StringRef Usr) : Usr(Usr) {} static void describe(llvm::raw_ostream &os) { os << "// OVERLOAD_FUNC_TRAILING_CLOSURE(USR)\n"; } static void undef(llvm::raw_ostream &os) { os << "#undef OVERLOAD_FUNC_TRAILING_CLOSURE\n"; } void streamDef(llvm::raw_ostream &os) const { os << "OVERLOAD_FUNC_TRAILING_CLOSURE(" << "\"" << Usr << "\"" << ")"; } bool operator<(OverloadedFuncInfo Other) const { return Usr.compare(Other.Usr) < 0; } }; /// This provides a utility to view a printed name by parsing the components /// of that name. The components include a base name and an array of argument /// labels. class DeclNameViewer { StringRef BaseName; SmallVector Labels; public: DeclNameViewer(StringRef Text) { auto ArgStart = Text.find_first_of('('); if (ArgStart == StringRef::npos) { BaseName = Text; return; } BaseName = Text.substr(0, ArgStart); auto ArgEnd = Text.find_last_of(')'); assert(ArgEnd != StringRef::npos); StringRef AllArgs = Text.substr(ArgStart + 1, ArgEnd - ArgStart - 1); AllArgs.split(Labels, ":"); if (Labels.empty()) return; assert(Labels.back().empty()); Labels.pop_back(); } StringRef base() const { return BaseName; } llvm::ArrayRef args() const { return llvm::makeArrayRef(Labels); } unsigned partsCount() const { return 1 + Labels.size(); } unsigned commonParts(DeclNameViewer &Other) const { if (base() != Other.base()) return 0; unsigned Result = 1; unsigned Len = std::min(args().size(), Other.args().size()); for (unsigned I = 0; I < Len; ++ I) { if (args()[I] == Other.args()[I]) Result ++; else return Result; } return Result; } }; class OverloadMemberFunctionEmitter : public SDKNodeVisitor { std::vector &AllItems; virtual void visit(NodePtr Node) override { if (Node->getKind() != SDKNodeKind::Function) return; auto Parent = Node->getParent(); if (Parent->getKind() != SDKNodeKind::TypeDecl) return; DeclNameViewer CurrentViewer(Node->getPrintedName()); if (CurrentViewer.args().empty()) return; for (auto &C : Parent->getChildren()) { if (C.get() == Node) continue; if (C->getKind() != SDKNodeKind::Function) continue; DeclNameViewer ChildViewer(C->getPrintedName()); if (ChildViewer.args().empty()) continue; if (CurrentViewer.commonParts(ChildViewer) >= CurrentViewer.partsCount() - 1) { AllItems.emplace_back(Node->getAs()->getUsr()); return; } } } OverloadMemberFunctionEmitter(std::vector &AllItems) : AllItems(AllItems) {} public: static void collectDiffItems(NodePtr Root, std::vector &AllItems) { OverloadMemberFunctionEmitter Emitter(AllItems); SDKNode::postorderVisit(Root, Emitter); } }; }// End of anonymous namespace namespace fs = llvm::sys::fs; namespace path = llvm::sys::path; static StringRef constructFullTypeName(NodePtr Node) { assert(Node->getKind() == SDKNodeKind::TypeDecl); std::vector TypeChain; for (auto C = Node; C->getKind() == SDKNodeKind::TypeDecl; C = C->getParent()) { TypeChain.insert(TypeChain.begin(), C); } assert(TypeChain.front()->getParent()->getKind() == SDKNodeKind::Root); llvm::SmallString<64> Buffer; bool First = true; for (auto N : TypeChain) { if (First) { First = false; } else { Buffer.append("."); } Buffer.append(N->getName()); } return InsertToBuffer(Buffer.str()); } struct RenameDetectorForMemberDiff : public MatchedNodeListener { void foundMatch(NodePtr Left, NodePtr Right) { detectRename(Left, Right); } void workOn(NodePtr Left, NodePtr Right) { if (Left->getKind() == Right->getKind() && Left->getKind() == SDKNodeKind::TypeDecl) { NodeVector LeftChildren; NodeVector RightChildren; Left->collectChildren(LeftChildren); Right->collectChildren(RightChildren); SameNameNodeMatcher Matcher(LeftChildren, RightChildren, *this); Matcher.match(); } } }; static Optional findSelfIndex(SDKNode* Node) { if (auto func = dyn_cast(Node)) { return func->getSelfIndexOptional(); } else if (auto vd = dyn_cast(Node)) { for (auto &C : vd->getChildren()) { if (isa(C.get())) { if (auto Result = findSelfIndex(C.get())) return Result; } } } return None; } /// Find cases where a diff is due to a change to being a type member static void findTypeMemberDiffs(NodePtr leftSDKRoot, NodePtr rightSDKRoot, TypeMemberDiffVector &out) { // Mapping from USR to SDKNode MapUSRToNode leftMapper; leftMapper.map(leftSDKRoot); auto &leftMap = leftMapper.getMap(); TypeMemberDiffFinder diffFinder(leftMap); diffFinder.findDiffsFor(rightSDKRoot); RenameDetectorForMemberDiff Detector; for (auto pair : diffFinder.getDiffs()) { auto left = pair.first; auto right = pair.second; auto rightParent = right->getParent(); // SDK_CHANGE_TYPE_MEMBER(USR, new type context name, new printed name, self // index, old printed name) TypeMemberDiffItem item = { right->getAs()->getUsr(), constructFullTypeName(rightParent), right->getPrintedName(), findSelfIndex(right), left->getPrintedName()}; out.emplace_back(item); Detector.workOn(left, right); } } static int diagnoseModuleChange(StringRef LeftPath, StringRef RightPath) { if (!fs::exists(LeftPath)) { llvm::errs() << LeftPath << " does not exist\n"; return 1; } if (!fs::exists(RightPath)) { llvm::errs() << RightPath << " does not exist\n"; return 1; } SwiftDeclCollector LeftCollector; LeftCollector.deSerialize(LeftPath); SwiftDeclCollector RightCollector; RightCollector.deSerialize(RightPath); auto LeftModule = LeftCollector.getSDKRoot(); auto RightModule = RightCollector.getSDKRoot(); PrunePass Prune; Prune.pass(LeftModule, RightModule); ChangeRefinementPass RefinementPass(Prune.getNodeUpdateMap()); RefinementPass.pass(LeftModule, RightModule); DiagnosisEmitter::diagnosis(LeftModule, RightModule, *RefinementPass.getNodeUpdateMap()); return 0; } static int compareSDKs(StringRef LeftPath, StringRef RightPath, StringRef DiffPath, llvm::StringSet<> &IgnoredRemoveUsrs) { if (!fs::exists(LeftPath)) { llvm::errs() << LeftPath << " does not exist\n"; return 1; } if (!fs::exists(RightPath)) { llvm::errs() << RightPath << " does not exist\n"; return 1; } llvm::errs() << "Diffing: " << LeftPath << " and " << RightPath << "\n"; SwiftDeclCollector LeftCollector; LeftCollector.deSerialize(LeftPath); SwiftDeclCollector RightCollector; RightCollector.deSerialize(RightPath); llvm::errs() << "Finished deserializing" << "\n"; auto LeftModule = LeftCollector.getSDKRoot(); auto RightModule = RightCollector.getSDKRoot(); // Structural diffs: not merely name changes but changes in SDK tree // structure. llvm::errs() << "Detecting type member diffs" << "\n"; TypeMemberDiffVector typeMemberDiffs; findTypeMemberDiffs(LeftModule, RightModule, typeMemberDiffs); PrunePass Prune; Prune.pass(LeftModule, RightModule); llvm::errs() << "Finished pruning" << "\n"; ChangeRefinementPass RefinementPass(Prune.getNodeUpdateMap()); RefinementPass.pass(LeftModule, RightModule); DiffVector AllItems; DiffItemEmitter::collectDiffItems(LeftModule, AllItems); AllItems.erase(std::remove_if(AllItems.begin(), AllItems.end(), [&](DiffItem &Item) { return Item.DiffKind == NodeAnnotation::RemovedDecl && IgnoredRemoveUsrs.find(Item.LeftUsr) != IgnoredRemoveUsrs.end(); }), AllItems.end()); NoEscapeFuncParamVector AllNoEscapingFuncs; NoEscapingFuncEmitter::collectDiffItems(RightModule, AllNoEscapingFuncs); llvm::errs() << "Dumping diff to " << DiffPath << '\n'; std::vector Overloads; OverloadMemberFunctionEmitter::collectDiffItems(RightModule, Overloads); std::error_code EC; llvm::raw_fd_ostream Fs(DiffPath, EC, llvm::sys::fs::F_None); serializeDiffs(Fs, AllItems); serializeDiffs(Fs, typeMemberDiffs); serializeDiffs(Fs, AllNoEscapingFuncs); serializeDiffs(Fs, Overloads); return 0; } static std::string getDumpFilePath(StringRef OutputDir, StringRef FileName) { std::string Path = OutputDir; Path += "/"; Path += FileName; int Suffix = 0; auto ConstructPath = [&]() { return Path + (Suffix == 0 ? "" : std::to_string(Suffix)) + ".js"; }; for (; fs::exists(ConstructPath()); Suffix ++); return ConstructPath(); } static int dumpSwiftModules(const CompilerInvocation &InitInvok, const llvm::StringSet<> &ModuleNames, StringRef OutputDir, const std::vector PrintApis) { if (!fs::exists(OutputDir)) { llvm::errs() << "Output directory '" << OutputDir << "' does not exist.\n"; return 1; } std::vector Modules; CompilerInvocation Invocation(InitInvok); CompilerInstance CI; // Display diagnostics to stderr. PrintingDiagnosticConsumer PrintDiags; CI.addDiagnosticConsumer(&PrintDiags); if (CI.setup(Invocation)) { llvm::errs() << "Failed to setup the compiler instance\n"; return 1; } auto &Context = CI.getASTContext(); for (auto &Entry : ModuleNames) { StringRef Name = Entry.first(); if (options::Verbose) llvm::errs() << "Loading module: " << Name << "...\n"; auto *M = Context.getModuleByName(Name); if (!M) { if (options::Verbose) llvm::errs() << "Failed to load module: " << Name << '\n'; if (options::AbortOnModuleLoadFailure) return 1; } Modules.push_back(M); } for (auto M : Modules) { SwiftDeclCollector Collector; SmallVector Decls; M->getTopLevelDecls(Decls); for (auto D : Decls) { if (auto VD = dyn_cast(D)) Collector.foundDecl(VD, DeclVisibilityKind::VisibleAtTopLevel); } std::string Path = getDumpFilePath(OutputDir, M->getName().str()); Collector.serialize(Path); if (options::Verbose) llvm::errs() << "Dumped to "<< Path << "\n"; } return 0; } static int dumpSDKContent(const CompilerInvocation &InitInvok, const llvm::StringSet<> &ModuleNames, StringRef OutputFile) { CompilerInvocation Invocation(InitInvok); CompilerInstance CI; // Display diagnostics to stderr. PrintingDiagnosticConsumer PrintDiags; CI.addDiagnosticConsumer(&PrintDiags); if (CI.setup(Invocation)) { llvm::errs() << "Failed to setup the compiler instance\n"; return 1; } auto &Ctx = CI.getASTContext(); // Load standard library so that Clang importer can use it. auto *Stdlib = Ctx.getStdlibModule(/*loadIfAbsent=*/true); if (!Stdlib) { llvm::errs() << "Failed to load Swift stdlib\n"; return 1; } std::vector Modules; for (auto &Entry : ModuleNames) { StringRef Name = Entry.getKey(); if (options::Verbose) llvm::errs() << "Loading module: " << Name << "...\n"; auto *M = Ctx.getModuleByName(Name); if (!M) { llvm::errs() << "Failed to load module: " << Name << '\n'; if (options::AbortOnModuleLoadFailure) return 1; } else { Modules.push_back(M); } } if (options::Verbose) llvm::errs() << "Scanning symbols...\n"; SwiftDeclCollector Collector; Collector.lookupVisibleDecls(Modules); if (options::Verbose) llvm::errs() << "Dumping SDK...\n"; Collector.serialize(OutputFile); if (options::Verbose) llvm::errs() << "Dumped to "<< OutputFile << "\n"; return 0; } static int readFileLineByLine(StringRef Path, llvm::StringSet<> &Lines) { auto FileBufOrErr = llvm::MemoryBuffer::getFile(Path); if (!FileBufOrErr) { llvm::errs() << "error opening file: " << FileBufOrErr.getError().message() << '\n'; return 1; } StringRef BufferText = FileBufOrErr.get()->getBuffer(); while (!BufferText.empty()) { StringRef Line; std::tie(Line, BufferText) = BufferText.split('\n'); Line = Line.trim(); if (!Line.empty()) Lines.insert(Line); } return 0; } // This function isn't referenced outside its translation unit, but it // can't use the "static" keyword because its address is used for // getMainExecutable (since some platforms don't support taking the // address of main, and some platforms can't implement getMainExecutable // without being given the address of a function in the main executable). void anchorForGetMainExecutable() {} static int prepareForDump(const char *Main, CompilerInvocation &InitInvok, llvm::StringSet<> &Modules) { InitInvok.setMainExecutablePath(fs::getMainExecutable(Main, reinterpret_cast(&anchorForGetMainExecutable))); InitInvok.setModuleName("swift_ide_test"); if (!options::SDK.empty()) { InitInvok.setSDKPath(options::SDK); } else if (const char *SDKROOT = getenv("SDKROOT")) { InitInvok.setSDKPath(SDKROOT); } else { llvm::errs() << "Provide '-sdk ' option or run with 'xcrun -sdk <..>\ swift-api-digester'\n"; return 1; } if (!options::Triple.empty()) InitInvok.setTargetTriple(options::Triple); InitInvok.getClangImporterOptions().ModuleCachePath = options::ModuleCachePath; if (!options::SwiftVersion.empty()) { if (auto Version = version::Version:: parseVersionString(options::SwiftVersion, SourceLoc(), nullptr)) { if (Version.getValue().isValidEffectiveLanguageVersion()) InitInvok.getLangOptions().EffectiveLanguageVersion = Version.getValue(); else { llvm::errs() << "Unsupported Swift Version.\n"; return 1; } } } if (!options::ResourceDir.empty()) { InitInvok.setRuntimeResourcePath(options::ResourceDir); } InitInvok.setFrameworkSearchPaths(options::FrameworkPaths); InitInvok.setImportSearchPaths(options::ModuleInputPaths); for (auto CCFrameworkPath : options::CCSystemFrameworkPaths) { InitInvok.getClangImporterOptions().ExtraArgs.push_back("-iframework"); InitInvok.getClangImporterOptions().ExtraArgs.push_back(CCFrameworkPath); } if (!options::ModuleList.empty()) { if (readFileLineByLine(options::ModuleList, Modules)) return 1; } for (auto M : options::ModuleNames) { Modules.insert(M); } if (Modules.empty()) { llvm::errs() << "Need to specify -include-all or -module \n"; return 1; } return 0; } static void readIgnoredUsrs(llvm::StringSet<> &IgnoredUsrs) { StringRef Path = options::IgnoreRemovedDeclUSRs; if (Path.empty()) return; if (!fs::exists(Path)) { llvm::errs() << Path << " does not exist.\n"; return; } readFileLineByLine(Path, IgnoredUsrs); } int main(int argc, char *argv[]) { INITIALIZE_LLVM(argc, argv); llvm::cl::ParseCommandLineOptions(argc, argv, "Swift SDK Digester\n"); CompilerInvocation InitInvok; ClangImporterOptions &ImporterOpts = InitInvok.getClangImporterOptions(); ImporterOpts.DetailedPreprocessingRecord = true; llvm::StringSet<> Modules; std::vector PrintApis; llvm::StringSet<> IgnoredUsrs; readIgnoredUsrs(IgnoredUsrs); for (auto Name : options::ApisPrintUsrs) PrintApis.push_back(Name); switch (options::Action) { case ActionType::DumpSwiftModules: return (prepareForDump(argv[0], InitInvok, Modules)) ? 1 : dumpSwiftModules(InitInvok, Modules, options::OutputFile, PrintApis); case ActionType::DumpSDK: return (prepareForDump(argv[0], InitInvok, Modules)) ? 1 : dumpSDKContent(InitInvok, Modules, options::OutputFile); case ActionType::CompareSDKs: case ActionType::DiagnoseSDKs: if (options::SDKJsonPaths.size() != 2) { llvm::errs() << "Only two SDK versions can be compared\n"; llvm::cl::PrintHelpMessage(); return 1; } if (options::Action == ActionType::CompareSDKs) return compareSDKs(options::SDKJsonPaths[0], options::SDKJsonPaths[1], options::OutputFile, IgnoredUsrs); else return diagnoseModuleChange(options::SDKJsonPaths[0], options::SDKJsonPaths[1]); case ActionType::None: llvm::errs() << "Action required\n"; llvm::cl::PrintHelpMessage(); return 1; } }