diff --git a/include/swift/APIDigester/ModuleAnalyzerNodes.h b/include/swift/APIDigester/ModuleAnalyzerNodes.h index 91cf567f93a4c..e0e3fb682bba1 100644 --- a/include/swift/APIDigester/ModuleAnalyzerNodes.h +++ b/include/swift/APIDigester/ModuleAnalyzerNodes.h @@ -23,6 +23,7 @@ #include "clang/Sema/Lookup.h" #include "clang/Sema/Sema.h" #include "llvm/ADT/TinyPtrVector.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/FileSystem.h" @@ -717,6 +718,12 @@ class SDKNodeDeclAccessor: public SDKNodeDeclAbstractFunc { void jsonize(json::Output &Out) override; }; +class SDKNodeDeclImport: public SDKNodeDecl { +public: + SDKNodeDeclImport(SDKNodeInitInfo Info); + static bool classof(const SDKNode *N); +}; + // The additional information we need for a type node in the digest. // We use type node to represent entities more than types, e.g. parameters, so // this struct is necessary to pass down to create a type node. @@ -781,6 +788,8 @@ class SwiftDeclCollector: public VisibleDeclConsumer { void lookupVisibleDecls(ArrayRef Modules); }; +void detectRename(SDKNode *L, SDKNode *R); + int dumpSwiftModules(const CompilerInvocation &InitInvok, const llvm::StringSet<> &ModuleNames, StringRef OutputDir, @@ -799,6 +808,8 @@ int dumpSDKContent(const CompilerInvocation &InitInvok, const llvm::StringSet<> &ModuleNames, StringRef OutputFile, CheckerOptions Opts); +void dumpModuleContent(ModuleDecl *MD, StringRef OutputFile, bool ABI); + /// Mostly for testing purposes, this function de-serializes the SDK dump in /// dumpPath and re-serialize them to OutputPath. If the tool performs correctly, /// the contents in dumpPath and OutputPath should be identical. diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index 11c20cb8e329d..7c7a4bb4c4454 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -129,6 +129,8 @@ ERROR(error_mode_cannot_emit_module_summary,none, "this mode does not support emitting module summary files", ()) ERROR(error_mode_cannot_emit_symbol_graph,none, "this mode does not support emitting symbol graph files", ()) +ERROR(error_mode_cannot_emit_abi_descriptor,none, + "this mode does not support emitting ABI descriptor", ()) ERROR(cannot_emit_ir_skipping_function_bodies,none, "the -experimental-skip-*-function-bodies* flags do not support " "emitting IR", ()) diff --git a/include/swift/AST/DiagnosticsModuleDiffer.def b/include/swift/AST/DiagnosticsModuleDiffer.def index dbcc194ba4ca6..4c66365d13ac9 100644 --- a/include/swift/AST/DiagnosticsModuleDiffer.def +++ b/include/swift/AST/DiagnosticsModuleDiffer.def @@ -88,6 +88,8 @@ ERROR(not_inheriting_convenience_inits,none,"%0 no longer inherits convenience i ERROR(enum_case_added,none,"%0 has been added as a new enum case", (StringRef)) +ERROR(demangled_name_changed,none,"%0 has mangled name changing from '%1' to '%2'", (StringRef, StringRef, StringRef)) + WARNING(cannot_read_allowlist,none,"cannot read breakage allowlist at '%0'", (StringRef)) #define UNDEFINE_DIAGNOSTIC_MACROS diff --git a/include/swift/AST/USRGeneration.h b/include/swift/AST/USRGeneration.h index 7b96c4d3e3c27..5f799a06abd02 100644 --- a/include/swift/AST/USRGeneration.h +++ b/include/swift/AST/USRGeneration.h @@ -21,6 +21,8 @@ #include "swift/Basic/LLVM.h" +#include + namespace swift { class Decl; class AbstractStorageDecl; @@ -61,6 +63,9 @@ bool printExtensionUSR(const ExtensionDecl *ED, raw_ostream &OS); /// \returns true if it failed, false on success. bool printDeclUSR(const Decl *D, raw_ostream &OS); +/// Demangle a mangle-name-based USR to a human readable name. +std::string demangleUSR(StringRef mangled); + } // namespace ide } // namespace swift diff --git a/include/swift/Basic/SupplementaryOutputPaths.h b/include/swift/Basic/SupplementaryOutputPaths.h index e72f0ecb02b10..54aabef9ffc9f 100644 --- a/include/swift/Basic/SupplementaryOutputPaths.h +++ b/include/swift/Basic/SupplementaryOutputPaths.h @@ -153,6 +153,9 @@ struct SupplementaryOutputPaths { /// The path to which we should emit module summary file. std::string ModuleSummaryOutputPath; + /// The output path to generate ABI baseline. + std::string ABIDescriptorOutputPath; + SupplementaryOutputPaths() = default; SupplementaryOutputPaths(const SupplementaryOutputPaths &) = default; @@ -186,6 +189,8 @@ struct SupplementaryOutputPaths { fn(LdAddCFilePath); if (!ModuleSummaryOutputPath.empty()) fn(ModuleSummaryOutputPath); + if (!ABIDescriptorOutputPath.empty()) + fn(ABIDescriptorOutputPath); } bool empty() const { @@ -194,7 +199,7 @@ struct SupplementaryOutputPaths { ReferenceDependenciesFilePath.empty() && SerializedDiagnosticsPath.empty() && LoadedModuleTracePath.empty() && TBDPath.empty() && ModuleInterfaceOutputPath.empty() && - ModuleSourceInfoOutputPath.empty() && LdAddCFilePath.empty(); + ModuleSourceInfoOutputPath.empty() && LdAddCFilePath.empty() && ABIDescriptorOutputPath.empty(); } }; } // namespace swift diff --git a/include/swift/Frontend/FrontendInputsAndOutputs.h b/include/swift/Frontend/FrontendInputsAndOutputs.h index 28f60519074b4..4ccb1fc14860d 100644 --- a/include/swift/Frontend/FrontendInputsAndOutputs.h +++ b/include/swift/Frontend/FrontendInputsAndOutputs.h @@ -256,6 +256,7 @@ class FrontendInputsAndOutputs { bool hasModuleSourceInfoOutputPath() const; bool hasModuleInterfaceOutputPath() const; bool hasPrivateModuleInterfaceOutputPath() const; + bool hasABIDescriptorOutputPath() const; bool hasModuleSummaryOutputPath() const; bool hasTBDPath() const; diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index 439d8dcbba538..8406058b87221 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -423,6 +423,7 @@ class FrontendOptions { static bool canActionEmitModuleDoc(ActionType); static bool canActionEmitModuleSummary(ActionType); static bool canActionEmitInterface(ActionType); + static bool canActionEmitABIDescriptor(ActionType); public: static bool doesActionGenerateSIL(ActionType); diff --git a/include/swift/Frontend/ModuleInterfaceLoader.h b/include/swift/Frontend/ModuleInterfaceLoader.h index 90469b2372b2e..f947c60859b38 100644 --- a/include/swift/Frontend/ModuleInterfaceLoader.h +++ b/include/swift/Frontend/ModuleInterfaceLoader.h @@ -414,7 +414,7 @@ class ModuleInterfaceLoader : public SerializedModuleLoaderBase { const SearchPathOptions &SearchPathOpts, const LangOptions &LangOpts, const ClangImporterOptions &ClangOpts, StringRef CacheDir, StringRef PrebuiltCacheDir, StringRef ModuleName, StringRef InPath, - StringRef OutPath, bool SerializeDependencyHashes, + StringRef OutPath, StringRef ABIOutputPath, bool SerializeDependencyHashes, bool TrackSystemDependencies, ModuleInterfaceLoaderOptions Opts, RequireOSSAModules_t RequireOSSAModules); }; diff --git a/include/swift/IDE/DigesterEnums.def b/include/swift/IDE/DigesterEnums.def index 415db666c919d..b58b49fe0b471 100644 --- a/include/swift/IDE/DigesterEnums.def +++ b/include/swift/IDE/DigesterEnums.def @@ -72,6 +72,7 @@ NODE_KIND(DeclOperator, OperatorDecl) NODE_KIND(DeclType, TypeDecl) NODE_KIND(DeclVar, Var) NODE_KIND(DeclTypeAlias, TypeAlias) +NODE_KIND(DeclImport, Import) NODE_KIND(DeclAssociatedType, AssociatedType) NODE_KIND_RANGE(Decl, DeclFunction, DeclAssociatedType) diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 3c1bd619c9be5..5252ae38f5be4 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -64,6 +64,10 @@ def emit_fixits_path : Separate<["-"], "emit-fixits-path">, MetaVarName<"">, HelpText<"Output compiler fixits as source edits to ">; +def emit_abi_descriptor_path + : Separate<["-"], "emit-abi-descriptor-path">, MetaVarName<"">, + HelpText<"Output the ABI descriptor of current module to ">; + def serialize_module_interface_dependency_hashes : Flag<["-"], "serialize-module-interface-dependency-hashes">, Flags<[HelpHidden]>; diff --git a/lib/APIDigester/ModuleAnalyzerNodes.cpp b/lib/APIDigester/ModuleAnalyzerNodes.cpp index 88ee93e380922..a1dc551375c2d 100644 --- a/lib/APIDigester/ModuleAnalyzerNodes.cpp +++ b/lib/APIDigester/ModuleAnalyzerNodes.cpp @@ -46,6 +46,7 @@ struct swift::ide::api::SDKNodeInitInfo { SDKNodeInitInfo(SDKContext &Ctx, Decl *D); SDKNodeInitInfo(SDKContext &Ctx, ValueDecl *VD); SDKNodeInitInfo(SDKContext &Ctx, OperatorDecl *D); + SDKNodeInitInfo(SDKContext &Ctx, ImportDecl *ID); SDKNodeInitInfo(SDKContext &Ctx, ProtocolConformance *Conform); SDKNodeInitInfo(SDKContext &Ctx, Type Ty, TypeInitInfo Info = TypeInitInfo()); SDKNode* createSDKNode(SDKNodeKind Kind); @@ -80,6 +81,9 @@ void SDKNodeRoot::registerDescendant(SDKNode *D) { // Operator doesn't have usr if (isa(D)) return; + // Import doesn't have usr + if (isa(D)) + return; if (auto DD = dyn_cast(D)) { assert(!DD->getUsr().empty()); DescendantDeclTable[DD->getUsr()].insert(DD); @@ -166,6 +170,9 @@ SDKNodeDeclAccessor::SDKNodeDeclAccessor(SDKNodeInitInfo Info): SDKNodeDeclAbstractFunc(Info, SDKNodeKind::DeclAccessor), AccKind(Info.AccKind) {} +SDKNodeDeclImport::SDKNodeDeclImport(SDKNodeInitInfo Info): + SDKNodeDecl(Info, SDKNodeKind::DeclImport) {} + SDKNodeDeclAssociatedType::SDKNodeDeclAssociatedType(SDKNodeInitInfo Info): SDKNodeDecl(Info, SDKNodeKind::DeclAssociatedType) {}; @@ -375,6 +382,7 @@ StringRef SDKNodeType::getTypeRoleDescription() const { case SDKNodeKind::DeclType: case SDKNodeKind::DeclOperator: case SDKNodeKind::Conformance: + case SDKNodeKind::DeclImport: llvm_unreachable("Type Parent is wrong"); case SDKNodeKind::DeclFunction: case SDKNodeKind::DeclConstructor: @@ -936,10 +944,13 @@ static bool isSDKNodeEqual(SDKContext &Ctx, const SDKNode &L, const SDKNode &R) if (Left->getFixedBinaryOrder() != Right->getFixedBinaryOrder()) return false; } + if (Left->getUsr() != Right->getUsr()) + return false; LLVM_FALLTHROUGH; } case SDKNodeKind::Conformance: case SDKNodeKind::TypeWitness: + case SDKNodeKind::DeclImport: case SDKNodeKind::Root: { return L.getPrintedName() == R.getPrintedName() && L.hasSameChildren(R); @@ -1358,6 +1369,13 @@ SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, OperatorDecl *OD): PrintedName = OD->getName().str(); } +SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, ImportDecl *ID): + SDKNodeInitInfo(Ctx, cast(ID)) { + std::string content; + llvm::raw_string_ostream OS(content); + ID->getModulePath().print(OS); + Name = PrintedName = Ctx.buffer(content); +} SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, ProtocolConformance *Conform): SDKNodeInitInfo(Ctx, Conform->getProtocol()) { @@ -1888,6 +1906,10 @@ void SwiftDeclCollector::processDecl(Decl *D) { if (auto *OD = dyn_cast(D)) { RootNode->addChild(constructOperatorDeclNode(OD)); } + if (auto *IM = dyn_cast(D)) { + RootNode->addChild(SDKNodeInitInfo(Ctx, IM) + .createSDKNode(SDKNodeKind::DeclImport)); + } } void SwiftDeclCollector::processValueDecl(ValueDecl *VD) { @@ -2274,6 +2296,22 @@ int swift::ide::api::deserializeSDKDump(StringRef dumpPath, StringRef OutputPath return 0; } +void swift::ide::api::dumpModuleContent(ModuleDecl *MD, StringRef OutputFile, + bool ABI) { + CheckerOptions opts; + opts.ABI = ABI; + opts.SwiftOnly = true; + opts.AvoidLocation = true; + opts.AvoidToolArgs = true; + opts.Migrator = false; + opts.SkipOSCheck = false; + opts.Verbose = false; + SDKContext ctx(opts); + SwiftDeclCollector collector(ctx); + collector.lookupVisibleDecls({MD}); + dumpSDKRoot(collector.getSDKRoot(), OutputFile); +} + int swift::ide::api::findDeclUsr(StringRef dumpPath, CheckerOptions Opts) { std::error_code EC; if (!fs::exists(dumpPath)) { @@ -2303,3 +2341,308 @@ int swift::ide::api::findDeclUsr(StringRef dumpPath, CheckerOptions Opts) { } return 0; } + +void swift::ide::api::SDKNodeDeclType::diagnose(SDKNode *Right) { + SDKNodeDecl::diagnose(Right); + auto *R = dyn_cast(Right); + if (!R) + return; + auto Loc = R->getLoc(); + if (getDeclKind() != R->getDeclKind()) { + emitDiag(Loc, diag::decl_kind_changed, getDeclKindStr(R->getDeclKind(), + getSDKContext().getOpts().CompilerStyle)); + return; + } + + assert(getDeclKind() == R->getDeclKind()); + auto DKind = getDeclKind(); + switch (DKind) { + case DeclKind::Class: { + auto LSuperClass = getSuperClassName(); + auto RSuperClass = R->getSuperClassName(); + if (!LSuperClass.empty() && LSuperClass != RSuperClass) { + if (RSuperClass.empty()) { + emitDiag(Loc, diag::super_class_removed, LSuperClass); + } else if (!llvm::is_contained(R->getClassInheritanceChain(), LSuperClass)) { + emitDiag(Loc, diag::super_class_changed, LSuperClass, RSuperClass); + } + } + + // Check for @_hasMissingDesignatedInitializers and + // @_inheritsConvenienceInitializers changes. + if (isOpen() && R->isOpen()) { + // It's not safe to add new, invisible designated inits to open + // classes. + if (!hasMissingDesignatedInitializers() && + R->hasMissingDesignatedInitializers()) + R->emitDiag(R->getLoc(), diag::added_invisible_designated_init); + } + + // It's not safe to stop inheriting convenience inits, it changes + // the set of initializers that are available. + if (!Ctx.checkingABI() && + inheritsConvenienceInitializers() && + !R->inheritsConvenienceInitializers()) + R->emitDiag(R->getLoc(), diag::not_inheriting_convenience_inits); + break; + } + default: + break; + } +} + +void swift::ide::api::SDKNodeDeclAbstractFunc::diagnose(SDKNode *Right) { + SDKNodeDecl::diagnose(Right); + auto *R = dyn_cast(Right); + if (!R) + return; + auto Loc = R->getLoc(); + if (!isThrowing() && R->isThrowing()) { + emitDiag(Loc, diag::decl_new_attr, Ctx.buffer("throwing")); + } + if (Ctx.checkingABI()) { + if (reqNewWitnessTableEntry() != R->reqNewWitnessTableEntry()) { + emitDiag(Loc, diag::decl_new_witness_table_entry, reqNewWitnessTableEntry()); + } + } +} + +void swift::ide::api::SDKNodeDeclFunction::diagnose(SDKNode *Right) { + SDKNodeDeclAbstractFunc::diagnose(Right); + auto *R = dyn_cast(Right); + if (!R) + return; + auto Loc = R->getLoc(); + if (getSelfAccessKind() != R->getSelfAccessKind()) { + emitDiag(Loc, diag::func_self_access_change, getSelfAccessKind(), + R->getSelfAccessKind()); + } + if (Ctx.checkingABI()) { + if (hasFixedBinaryOrder() != R->hasFixedBinaryOrder()) { + emitDiag(Loc, diag::func_has_fixed_order_change, hasFixedBinaryOrder()); + } + } +} + +static StringRef getAttrName(DeclAttrKind Kind) { + switch (Kind) { +#define DECL_ATTR(NAME, CLASS, ...) \ + case DAK_##CLASS: \ + return DeclAttribute::isDeclModifier(DAK_##CLASS) ? #NAME : "@"#NAME; +#include "swift/AST/Attr.def" + case DAK_Count: + llvm_unreachable("unrecognized attribute kind."); + } + llvm_unreachable("covered switch"); +} + +static bool shouldDiagnoseAddingAttribute(SDKNodeDecl *D, DeclAttrKind Kind) { + return true; +} + +static bool shouldDiagnoseRemovingAttribute(SDKNodeDecl *D, DeclAttrKind Kind) { + return true; +} + +static bool isOwnershipEquivalent(ReferenceOwnership Left, + ReferenceOwnership Right) { + if (Left == Right) + return true; + if (Left == ReferenceOwnership::Unowned && Right == ReferenceOwnership::Weak) + return true; + if (Left == ReferenceOwnership::Weak && Right == ReferenceOwnership::Unowned) + return true; + return false; +} + +void swift::ide::api::detectRename(SDKNode *L, SDKNode *R) { + if (L->getKind() == R->getKind() && isa(L) && + L->getPrintedName() != R->getPrintedName()) { + L->annotate(NodeAnnotation::Rename); + L->annotate(NodeAnnotation::RenameOldName, L->getPrintedName()); + L->annotate(NodeAnnotation::RenameNewName, R->getPrintedName()); + } +} + +void swift::ide::api::SDKNodeDecl::diagnose(SDKNode *Right) { + SDKNode::diagnose(Right); + auto *RD = dyn_cast(Right); + if (!RD) + return; + detectRename(this, RD); + auto Loc = RD->getLoc(); + if (isOpen() && !RD->isOpen()) { + emitDiag(Loc, diag::no_longer_open); + } + + // Diagnose static attribute change. + if (isStatic() ^ RD->isStatic()) { + emitDiag(Loc, diag::decl_new_attr, Ctx.buffer(isStatic() ? "not static" : + "static")); + } + + // Diagnose ownership change. + if (!isOwnershipEquivalent(getReferenceOwnership(), + RD->getReferenceOwnership())) { + auto getOwnershipDescription = [&](swift::ReferenceOwnership O) { + if (O == ReferenceOwnership::Strong) + return Ctx.buffer("strong"); + return keywordOf(O); + }; + emitDiag(Loc, diag::decl_attr_change, + getOwnershipDescription(getReferenceOwnership()), + getOwnershipDescription(RD->getReferenceOwnership())); + } + // Diagnose generic signature change + if (getGenericSignature() != RD->getGenericSignature()) { + // Prefer sugared signature in diagnostics to be more user-friendly. + if (Ctx.commonVersionAtLeast(2) && + getSugaredGenericSignature() != RD->getSugaredGenericSignature()) { + emitDiag(Loc, diag::generic_sig_change, + getSugaredGenericSignature(), RD->getSugaredGenericSignature()); + } else { + emitDiag(Loc, diag::generic_sig_change, + getGenericSignature(), RD->getGenericSignature()); + } + } + + // ObjC name changes are considered breakage + if (getObjCName() != RD->getObjCName()) { + if (Ctx.commonVersionAtLeast(4)) { + emitDiag(Loc, diag::objc_name_change, getObjCName(), RD->getObjCName()); + } + } + + if (isOptional() != RD->isOptional()) { + if (Ctx.checkingABI()) { + // Both adding/removing optional is ABI-breaking. + emitDiag(Loc, diag::optional_req_changed, isOptional()); + } else if (isOptional()) { + // Removing optional is source-breaking. + emitDiag(Loc, diag::optional_req_changed, isOptional()); + } + } + + // Diagnose removing attributes. + for (auto Kind: getDeclAttributes()) { + if (!RD->hasDeclAttribute(Kind)) { + if ((Ctx.checkingABI() ? DeclAttribute::isRemovingBreakingABI(Kind) : + DeclAttribute::isRemovingBreakingAPI(Kind)) && + shouldDiagnoseRemovingAttribute(this, Kind)) { + emitDiag(Loc, diag::decl_new_attr, + Ctx.buffer((llvm::Twine("without ") + getAttrName(Kind)).str())); + } + } + } + + // Diagnose adding attributes. + for (auto Kind: RD->getDeclAttributes()) { + if (!hasDeclAttribute(Kind)) { + if ((Ctx.checkingABI() ? DeclAttribute::isAddingBreakingABI(Kind) : + DeclAttribute::isAddingBreakingAPI(Kind)) && + shouldDiagnoseAddingAttribute(this, Kind)) { + emitDiag(Loc, diag::decl_new_attr, + Ctx.buffer((llvm::Twine("with ") + getAttrName(Kind)).str())); + } + } + } + + if (Ctx.checkingABI()) { + if (hasFixedBinaryOrder() && RD->hasFixedBinaryOrder() && + getFixedBinaryOrder() != RD->getFixedBinaryOrder()) { + emitDiag(Loc, diag::decl_reorder, getFixedBinaryOrder(), + RD->getFixedBinaryOrder()); + } + if (getUsr() != RD->getUsr()) { + auto left = demangleUSR(getUsr()); + auto right = demangleUSR(RD->getUsr()); + if (left != right) { + emitDiag(Loc, diag::demangled_name_changed, left, right); + } + } + } +} + +void swift::ide::api::SDKNodeDeclOperator::diagnose(SDKNode *Right) { + SDKNodeDecl::diagnose(Right); + auto *RO = dyn_cast(Right); + if (!RO) + return; + auto Loc = RO->getLoc(); + if (getDeclKind() != RO->getDeclKind()) { + emitDiag(Loc, diag::decl_kind_changed, getDeclKindStr(RO->getDeclKind(), + getSDKContext().getOpts().CompilerStyle)); + } +} + +void swift::ide::api::SDKNodeDeclVar::diagnose(SDKNode *Right) { + SDKNodeDecl::diagnose(Right); + auto *RV = dyn_cast(Right); + if (!RV) + return; + auto Loc = RV->getLoc(); + if (Ctx.checkingABI()) { + if (hasFixedBinaryOrder() != RV->hasFixedBinaryOrder()) { + emitDiag(Loc, diag::var_has_fixed_order_change, hasFixedBinaryOrder()); + } + } +} + +static bool shouldDiagnoseType(SDKNodeType *T) { + return T->isTopLevelType(); +} + +void swift::ide::api::SDKNodeType::diagnose(SDKNode *Right) { + SDKNode::diagnose(Right); + auto *RT = dyn_cast(Right); + if (!RT || !shouldDiagnoseType(this)) + return; + assert(isTopLevelType()); + + // Diagnose type witness changes when diagnosing ABI breakages. + if (auto *Wit = dyn_cast(getParent())) { + auto *Conform = Wit->getParent()->getAs(); + if (Ctx.checkingABI() && getPrintedName() != RT->getPrintedName()) { + auto *LD = Conform->getNominalTypeDecl(); + LD->emitDiag(SourceLoc(), diag::type_witness_change, + Wit->getWitnessedTypeName(), + getPrintedName(), RT->getPrintedName()); + } + return; + } + + StringRef Descriptor = getTypeRoleDescription(); + assert(isa(getParent())); + auto LParent = cast(getParent()); + assert(LParent->getKind() == RT->getParent()->getAs()->getKind()); + auto Loc = RT->getParent()->getAs()->getLoc(); + if (getPrintedName() != RT->getPrintedName()) { + LParent->emitDiag(Loc, diag::decl_type_change, + Descriptor, getPrintedName(), RT->getPrintedName()); + } + + if (hasDefaultArgument() && !RT->hasDefaultArgument()) { + LParent->emitDiag(Loc, diag::default_arg_removed, Descriptor); + } + if (getParamValueOwnership() != RT->getParamValueOwnership()) { + LParent->emitDiag(Loc, diag::param_ownership_change, + getTypeRoleDescription(), + getParamValueOwnership(), + RT->getParamValueOwnership()); + } +} + +void swift::ide::api::SDKNodeTypeFunc::diagnose(SDKNode *Right) { + SDKNodeType::diagnose(Right); + auto *RT = dyn_cast(Right); + if (!RT || !shouldDiagnoseType(this)) + return; + assert(isTopLevelType()); + auto Loc = RT->getParent()->getAs()->getLoc(); + if (Ctx.checkingABI() && isEscaping() != RT->isEscaping()) { + getParent()->getAs()->emitDiag(Loc, + diag::func_type_escaping_changed, + getTypeRoleDescription(), + isEscaping()); + } +} diff --git a/lib/APIDigester/ModuleDiagsConsumer.cpp b/lib/APIDigester/ModuleDiagsConsumer.cpp index 044c9d05926d9..c73fd148d5947 100644 --- a/lib/APIDigester/ModuleDiagsConsumer.cpp +++ b/lib/APIDigester/ModuleDiagsConsumer.cpp @@ -54,6 +54,7 @@ static StringRef getCategoryName(uint32_t ID) { case LocalDiagID::raw_type_change: return "/* RawRepresentable Changes */"; case LocalDiagID::generic_sig_change: + case LocalDiagID::demangled_name_changed: return "/* Generic Signature Changes */"; case LocalDiagID::enum_case_added: case LocalDiagID::decl_added: diff --git a/lib/AST/USRGeneration.cpp b/lib/AST/USRGeneration.cpp index c3fe2322c21c9..5a02175895e8f 100644 --- a/lib/AST/USRGeneration.cpp +++ b/lib/AST/USRGeneration.cpp @@ -19,6 +19,7 @@ #include "swift/AST/SwiftNameTranslation.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/AST/USRGeneration.h" +#include "swift/Demangling/Demangler.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/raw_ostream.h" @@ -255,6 +256,18 @@ swift::USRGenerationRequest::evaluate(Evaluator &evaluator, return NewMangler.mangleDeclAsUSR(D, getUSRSpacePrefix()); } +std::string ide::demangleUSR(StringRef mangled) { + if (mangled.startswith(getUSRSpacePrefix())) { + mangled = mangled.substr(getUSRSpacePrefix().size()); + } + SmallString<128> buffer; + buffer += "$s"; + buffer += mangled; + mangled = buffer.str(); + Demangler Dem; + return nodeToString(Dem.demangleSymbol(mangled)); +} + std::string swift::MangleLocalTypeDeclRequest::evaluate(Evaluator &evaluator, const TypeDecl *D) const { diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index adf4df00b0e6f..bf9b030b9a473 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -594,6 +594,11 @@ bool ArgsToFrontendOptionsConverter::checkUnusedSupplementaryOutputPaths() Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_module_doc); return true; } + if (!FrontendOptions::canActionEmitABIDescriptor(Opts.RequestedAction) && + Opts.InputsAndOutputs.hasABIDescriptorOutputPath()) { + Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_abi_descriptor); + return true; + } // If we cannot emit module doc, we cannot emit source information file either. if (!FrontendOptions::canActionEmitModuleDoc(Opts.RequestedAction) && Opts.InputsAndOutputs.hasModuleSourceInfoOutputPath()) { diff --git a/lib/Frontend/ArgsToFrontendOutputsConverter.cpp b/lib/Frontend/ArgsToFrontendOutputsConverter.cpp index 0c042e7a12d05..0d95a416d0120 100644 --- a/lib/Frontend/ArgsToFrontendOutputsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOutputsConverter.cpp @@ -341,11 +341,14 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() options::OPT_emit_ldadd_cfile_path); auto moduleSummaryOutput = getSupplementaryFilenamesFromArguments( options::OPT_emit_module_summary_path); + auto abiDescriptorOutput = getSupplementaryFilenamesFromArguments( + options::OPT_emit_abi_descriptor_path); if (!objCHeaderOutput || !moduleOutput || !moduleDocOutput || !dependenciesFile || !referenceDependenciesFile || !serializedDiagnostics || !fixItsOutput || !loadedModuleTrace || !TBD || !moduleInterfaceOutput || !privateModuleInterfaceOutput || - !moduleSourceInfoOutput || !ldAddCFileOutput || !moduleSummaryOutput) { + !moduleSourceInfoOutput || !ldAddCFileOutput || !moduleSummaryOutput || + !abiDescriptorOutput) { return None; } std::vector result; @@ -368,6 +371,7 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() sop.ModuleSourceInfoOutputPath = (*moduleSourceInfoOutput)[i]; sop.LdAddCFilePath = (*ldAddCFileOutput)[i]; sop.ModuleSummaryOutputPath = (*moduleSummaryOutput)[i]; + sop.ABIDescriptorOutputPath = (*abiDescriptorOutput)[i]; result.push_back(sop); } return result; @@ -466,6 +470,8 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput( auto PrivateModuleInterfaceOutputPath = pathsFromArguments.PrivateModuleInterfaceOutputPath; + // There is no non-path form of -emit-abi-descriptor-path + auto ABIDescriptorOutputPath = pathsFromArguments.ABIDescriptorOutputPath; ID emitModuleOption; std::string moduleExtension; std::string mainOutputIfUsableForModule; @@ -492,6 +498,7 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput( sop.ModuleSourceInfoOutputPath = moduleSourceInfoOutputPath; sop.LdAddCFilePath = pathsFromArguments.LdAddCFilePath; sop.ModuleSummaryOutputPath = moduleSummaryOutputPath; + sop.ABIDescriptorOutputPath = ABIDescriptorOutputPath; return sop; } diff --git a/lib/Frontend/CMakeLists.txt b/lib/Frontend/CMakeLists.txt index 2c8c24f9e4dd9..c5ed0608d248d 100644 --- a/lib/Frontend/CMakeLists.txt +++ b/lib/Frontend/CMakeLists.txt @@ -27,5 +27,6 @@ target_link_libraries(swiftFrontend PRIVATE swiftLocalization swiftSema swiftSerialization - swiftTBDGen) + swiftTBDGen + swiftAPIDigester) diff --git a/lib/Frontend/FrontendInputsAndOutputs.cpp b/lib/Frontend/FrontendInputsAndOutputs.cpp index c65b43e6543be..d76a2db1b0a6d 100644 --- a/lib/Frontend/FrontendInputsAndOutputs.cpp +++ b/lib/Frontend/FrontendInputsAndOutputs.cpp @@ -503,6 +503,12 @@ bool FrontendInputsAndOutputs::hasPrivateModuleInterfaceOutputPath() const { return outs.PrivateModuleInterfaceOutputPath; }); } +bool FrontendInputsAndOutputs::hasABIDescriptorOutputPath() const { + return hasSupplementaryOutputPath( + [](const SupplementaryOutputPaths &outs) -> const std::string & { + return outs.ABIDescriptorOutputPath; + }); +} bool FrontendInputsAndOutputs::hasModuleSummaryOutputPath() const { return hasSupplementaryOutputPath( [](const SupplementaryOutputPaths &outs) -> const std::string & { diff --git a/lib/Frontend/FrontendOptions.cpp b/lib/Frontend/FrontendOptions.cpp index 452c1e5b99f87..08e0d14243cf1 100644 --- a/lib/Frontend/FrontendOptions.cpp +++ b/lib/Frontend/FrontendOptions.cpp @@ -524,7 +524,47 @@ bool FrontendOptions::canActionEmitLoadedModuleTrace(ActionType action) { } llvm_unreachable("unhandled action"); } - +bool FrontendOptions::canActionEmitABIDescriptor(ActionType action) { + switch (action) { + case ActionType::CompileModuleFromInterface: + return true; + case ActionType::NoneAction: + case ActionType::Parse: + case ActionType::ResolveImports: + case ActionType::Typecheck: + case ActionType::DumpParse: + case ActionType::DumpInterfaceHash: + case ActionType::DumpAST: + case ActionType::EmitSyntax: + case ActionType::PrintAST: + case ActionType::EmitPCH: + case ActionType::DumpScopeMaps: + case ActionType::DumpTypeRefinementContexts: + case ActionType::DumpTypeInfo: + case ActionType::EmitSILGen: + case ActionType::TypecheckModuleFromInterface: + case ActionType::Immediate: + case ActionType::REPL: + case ActionType::EmitPCM: + case ActionType::DumpPCM: + case ActionType::ScanDependencies: + case ActionType::PrintVersion: + case ActionType::PrintFeature: + case ActionType::MergeModules: + case ActionType::EmitModuleOnly: + case ActionType::EmitSIL: + case ActionType::EmitSIBGen: + case ActionType::EmitSIB: + case ActionType::EmitIRGen: + case ActionType::EmitIR: + case ActionType::EmitBC: + case ActionType::EmitAssembly: + case ActionType::EmitObject: + case ActionType::EmitImportedModules: + return false; + } + llvm_unreachable("unhandled action"); +} bool FrontendOptions::canActionEmitModule(ActionType action) { switch (action) { case ActionType::NoneAction: diff --git a/lib/Frontend/ModuleInterfaceBuilder.cpp b/lib/Frontend/ModuleInterfaceBuilder.cpp index 34339aab3f649..50b5c02b5eb30 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.cpp +++ b/lib/Frontend/ModuleInterfaceBuilder.cpp @@ -24,6 +24,7 @@ #include "swift/Frontend/ModuleInterfaceSupport.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/Serialization/SerializationOptions.h" +#include "swift/APIDigester/ModuleAnalyzerNodes.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/PreprocessorOptions.h" #include "llvm/ADT/Hashing.h" @@ -278,6 +279,9 @@ bool ModuleInterfaceBuilder::buildSwiftModuleInternal( if (SubInstance.getDiags().hadAnyError()) { return std::make_error_code(std::errc::not_supported); } + if (!ABIDescriptorPath.empty()) { + swift::ide::api::dumpModuleContent(Mod, ABIDescriptorPath, true); + } return std::error_code(); }); }, ThreadStackSize); diff --git a/lib/Frontend/ModuleInterfaceBuilder.h b/lib/Frontend/ModuleInterfaceBuilder.h index ae90b2ded60f9..ea73c5d235d3e 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.h +++ b/lib/Frontend/ModuleInterfaceBuilder.h @@ -41,6 +41,7 @@ class ModuleInterfaceBuilder { const StringRef moduleName; const StringRef moduleCachePath; const StringRef prebuiltCachePath; + const StringRef ABIDescriptorPath; const bool disableInterfaceFileLock; const SourceLoc diagnosticLoc; DependencyTracker *const dependencyTracker; @@ -94,6 +95,7 @@ class ModuleInterfaceBuilder { StringRef moduleName, StringRef moduleCachePath, StringRef prebuiltCachePath, + StringRef ABIDescriptorPath, bool disableInterfaceFileLock = false, SourceLoc diagnosticLoc = SourceLoc(), DependencyTracker *tracker = nullptr) @@ -101,6 +103,7 @@ class ModuleInterfaceBuilder { subASTDelegate(subASTDelegate), interfacePath(interfacePath), moduleName(moduleName), moduleCachePath(moduleCachePath), prebuiltCachePath(prebuiltCachePath), + ABIDescriptorPath(ABIDescriptorPath), disableInterfaceFileLock(disableInterfaceFileLock), diagnosticLoc(diagnosticLoc), dependencyTracker(tracker) {} diff --git a/lib/Frontend/ModuleInterfaceLoader.cpp b/lib/Frontend/ModuleInterfaceLoader.cpp index 83d4517fe8850..e9770d704364b 100644 --- a/lib/Frontend/ModuleInterfaceLoader.cpp +++ b/lib/Frontend/ModuleInterfaceLoader.cpp @@ -918,7 +918,7 @@ class ModuleInterfaceLoaderImpl { // the genericSubInvocation we'll need to use to compute the cache paths. ModuleInterfaceBuilder builder( ctx.SourceMgr, ctx.Diags, astDelegate, interfacePath, moduleName, cacheDir, - prebuiltCacheDir, + prebuiltCacheDir, StringRef(), Opts.disableInterfaceLock, diagnosticLoc, dependencyTracker); @@ -1093,7 +1093,7 @@ bool ModuleInterfaceLoader::buildSwiftModuleFromSwiftInterface( const SearchPathOptions &SearchPathOpts, const LangOptions &LangOpts, const ClangImporterOptions &ClangOpts, StringRef CacheDir, StringRef PrebuiltCacheDir, StringRef ModuleName, StringRef InPath, - StringRef OutPath, bool SerializeDependencyHashes, + StringRef OutPath, StringRef ABIOutputPath, bool SerializeDependencyHashes, bool TrackSystemDependencies, ModuleInterfaceLoaderOptions LoaderOpts, RequireOSSAModules_t RequireOSSAModules) { InterfaceSubContextDelegateImpl astDelegate( @@ -1101,7 +1101,7 @@ bool ModuleInterfaceLoader::buildSwiftModuleFromSwiftInterface( /*CreateCacheDirIfAbsent*/ true, CacheDir, PrebuiltCacheDir, SerializeDependencyHashes, TrackSystemDependencies, RequireOSSAModules); ModuleInterfaceBuilder builder(SourceMgr, Diags, astDelegate, InPath, - ModuleName, CacheDir, PrebuiltCacheDir, + ModuleName, CacheDir, PrebuiltCacheDir, ABIOutputPath, LoaderOpts.disableInterfaceLock); // FIXME: We really only want to serialize 'important' dependencies here, if // we want to ship the built swiftmodules to another machine. diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index a68c87cd55295..b01c4c85a5cff 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -412,12 +412,14 @@ static bool buildModuleFromInterface(CompilerInstance &Instance) { StringRef InputPath = FEOpts.InputsAndOutputs.getFilenameOfFirstInput(); StringRef PrebuiltCachePath = FEOpts.PrebuiltModuleCachePath; ModuleInterfaceLoaderOptions LoaderOpts(FEOpts); + StringRef ABIPath = Instance.getPrimarySpecificPathsForAtMostOnePrimary() + .SupplementaryOutputs.ABIDescriptorOutputPath; return ModuleInterfaceLoader::buildSwiftModuleFromSwiftInterface( Instance.getSourceMgr(), Instance.getDiags(), Invocation.getSearchPathOptions(), Invocation.getLangOptions(), Invocation.getClangImporterOptions(), Invocation.getClangModuleCachePath(), PrebuiltCachePath, - Invocation.getModuleName(), InputPath, Invocation.getOutputFilename(), + Invocation.getModuleName(), InputPath, Invocation.getOutputFilename(), ABIPath, FEOpts.SerializeModuleInterfaceDependencyHashes, FEOpts.shouldTrackSystemDependencies(), LoaderOpts, RequireOSSAModules_t(Invocation.getSILOptions())); diff --git a/test/ModuleInterface/emit-abi-descriptor.swift b/test/ModuleInterface/emit-abi-descriptor.swift new file mode 100644 index 0000000000000..530d5e5b110ec --- /dev/null +++ b/test/ModuleInterface/emit-abi-descriptor.swift @@ -0,0 +1,13 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/Foo.swiftmodule) +// RUN: %empty-directory(%t/ResourceDir/%target-sdk-name/prebuilt-modules/Foo.swiftmodule) +// RUN: echo "public func foo() {}" > %t/Foo.swift + +// RUN: %target-swift-frontend -emit-module %t/Foo.swift -module-name Foo -emit-module-interface-path %t/Foo.swiftinterface +// RUN: %target-swift-frontend -compile-module-from-interface %t/Foo.swiftinterface -o %t/Foo.swiftmodule -module-name Foo -emit-abi-descriptor-path %t/Foo.json + +// RUN: %FileCheck %s < %t/Foo.json + +// CHECK: "kind": "Root" +// CHECK-NEXT: "name": "TopLevel" +// CHECK-NEXT: "printedName": "TopLevel" diff --git a/test/api-digester/Outputs/Cake-abi.txt b/test/api-digester/Outputs/Cake-abi.txt index 6efaa9a4c05bc..7010f162d7149 100644 --- a/test/api-digester/Outputs/Cake-abi.txt +++ b/test/api-digester/Outputs/Cake-abi.txt @@ -1,7 +1,17 @@ /* Generic Signature Changes */ +cake: Constructor S1.init(_:) has mangled name changing from 'cake.S1.init(Swift.Int) -> cake.S1' to 'cake.S1.init(Swift.Double) -> cake.S1' +cake: Func C1.foo1() has mangled name changing from 'static cake.C1.foo1() -> ()' to 'cake.C1.foo1() -> ()' +cake: Func C1.foo2(_:) has mangled name changing from 'cake.C1.foo2(Swift.Int) -> ()' to 'cake.C1.foo2(() -> ()) -> ()' cake: Func P1.P1Constraint() has generic signature change from to +cake: Func P1.P1Constraint() has mangled name changing from '(extension in cake):cake.P1< where A: cake.P2>.P1Constraint() -> ()' to '(extension in cake):cake.P1.P1Constraint() -> ()' +cake: Func S1.foo3() has mangled name changing from 'cake.S1.foo3() -> ()' to 'static cake.S1.foo3() -> ()' +cake: Func S1.foo5(x:y:) has mangled name changing from 'cake.S1.foo5(x: Swift.Int, y: Swift.Int) -> ()' to 'cake.S1.foo5(x: Swift.Int, y: Swift.Int, z: Swift.Int) -> ()' +cake: Func Somestruct2.foo1(_:) has mangled name changing from 'static cake.Somestruct2.foo1(cake.C3) -> ()' to 'static cake.NSSomestruct2.foo1(cake.C1) -> ()' +cake: Func ownershipChange(_:_:) has mangled name changing from 'cake.ownershipChange(inout Swift.Int, __shared Swift.Int) -> ()' to 'cake.ownershipChange(Swift.Int, __owned Swift.Int) -> ()' +cake: Func returnFunctionTypeOwnershipChange() has mangled name changing from 'cake.returnFunctionTypeOwnershipChange() -> (cake.C1) -> ()' to 'cake.returnFunctionTypeOwnershipChange() -> (__owned cake.C1) -> ()' cake: Protocol P3 has generic signature change from to +cake: Struct Somestruct2 has mangled name changing from 'cake.Somestruct2' to 'cake.NSSomestruct2' /* RawRepresentable Changes */ @@ -112,6 +122,5 @@ cake: Var RequiementChanges.addedVar has been added as a protocol requirement /* Class Inheritance Change */ cake: Class SubGenericClass has changed its super class from cake.GenericClass to cake.GenericClass cake: Class SuperClassRemoval has removed its super class cake.C3 -cake: Class SuperClassRemoval no longer inherits convenience inits from its superclass cake: Constructor AddingNewDesignatedInit.init(_:) has been added as a designated initializer to an open class cake: Constructor ClassWithMissingDesignatedInits.init() has been added as a designated initializer to an open class diff --git a/test/api-digester/Outputs/apinotes-diags.txt b/test/api-digester/Outputs/apinotes-diags.txt index 7fc6938e7ffe3..b9db649487959 100644 --- a/test/api-digester/Outputs/apinotes-diags.txt +++ b/test/api-digester/Outputs/apinotes-diags.txt @@ -15,6 +15,7 @@ APINotesTest(APINotesTest.h): TypeAlias CatAttributeName has been removed APINotesTest(APINotesTest.h): Protocol SwiftTypeWithMethodLeft has been renamed to Protocol SwiftTypeWithMethodRight APINotesTest(APINotesTest.h): Var OldType.oldMember has been renamed to Var NewType.newMember APINotesTest(APINotesTest.h): Var globalAttributeName has been renamed to Var AnimalAttributeName.globalAttributeName +APINotesTest: Import Foundation has been renamed to Import objc_generics /* Type Changes */ APINotesTest(APINotesTest.h): Constructor Cat.init(name:) has return type change from APINotesTest.Cat to APINotesTest.Cat? diff --git a/test/api-digester/Outputs/apinotes-migrator-gen-revert.json b/test/api-digester/Outputs/apinotes-migrator-gen-revert.json index 3709410ff5d11..ff0d898e9df12 100644 --- a/test/api-digester/Outputs/apinotes-migrator-gen-revert.json +++ b/test/api-digester/Outputs/apinotes-migrator-gen-revert.json @@ -1,4 +1,15 @@ [ + { + "DiffItemKind": "CommonDiffItem", + "NodeKind": "Import", + "NodeAnnotation": "Rename", + "ChildIndex": "0", + "LeftUsr": "", + "LeftComment": "objc_generics", + "RightUsr": "", + "RightComment": "Foundation", + "ModuleName": "APINotesTest" + }, { "DiffItemKind": "CommonDiffItem", "NodeKind": "Var", diff --git a/test/api-digester/Outputs/apinotes-migrator-gen.json b/test/api-digester/Outputs/apinotes-migrator-gen.json index b0c6eb4d837e7..1cdde964dfb86 100644 --- a/test/api-digester/Outputs/apinotes-migrator-gen.json +++ b/test/api-digester/Outputs/apinotes-migrator-gen.json @@ -1,4 +1,15 @@ [ + { + "DiffItemKind": "CommonDiffItem", + "NodeKind": "Import", + "NodeAnnotation": "Rename", + "ChildIndex": "0", + "LeftUsr": "", + "LeftComment": "Foundation", + "RightUsr": "", + "RightComment": "objc_generics", + "ModuleName": "APINotesTest" + }, { "DiffItemKind": "CommonDiffItem", "NodeKind": "Var", diff --git a/test/api-digester/Outputs/cake-abi.json b/test/api-digester/Outputs/cake-abi.json index 66c95aaf78752..46b2e407e05b2 100644 --- a/test/api-digester/Outputs/cake-abi.json +++ b/test/api-digester/Outputs/cake-abi.json @@ -3,6 +3,30 @@ "name": "TopLevel", "printedName": "TopLevel", "children": [ + { + "kind": "Import", + "name": "SwiftOnoneSupport", + "printedName": "SwiftOnoneSupport", + "declKind": "Import", + "moduleName": "cake" + }, + { + "kind": "Import", + "name": "_Concurrency", + "printedName": "_Concurrency", + "declKind": "Import", + "moduleName": "cake" + }, + { + "kind": "Import", + "name": "cake", + "printedName": "cake", + "declKind": "Import", + "moduleName": "cake", + "declAttributes": [ + "Exported" + ] + }, { "kind": "TypeDecl", "name": "P1", diff --git a/test/api-digester/Outputs/cake.json b/test/api-digester/Outputs/cake.json index 7bbedca20bd0d..e7b79d34b5f72 100644 --- a/test/api-digester/Outputs/cake.json +++ b/test/api-digester/Outputs/cake.json @@ -3,6 +3,30 @@ "name": "TopLevel", "printedName": "TopLevel", "children": [ + { + "kind": "Import", + "name": "SwiftOnoneSupport", + "printedName": "SwiftOnoneSupport", + "declKind": "Import", + "moduleName": "cake" + }, + { + "kind": "Import", + "name": "_Concurrency", + "printedName": "_Concurrency", + "declKind": "Import", + "moduleName": "cake" + }, + { + "kind": "Import", + "name": "cake", + "printedName": "cake", + "declKind": "Import", + "moduleName": "cake", + "declAttributes": [ + "Exported" + ] + }, { "kind": "TypeDecl", "name": "P1", diff --git a/test/api-digester/Outputs/clang-module-dump.txt b/test/api-digester/Outputs/clang-module-dump.txt index 7ce4dff0f1672..2e858d64ec1c3 100644 --- a/test/api-digester/Outputs/clang-module-dump.txt +++ b/test/api-digester/Outputs/clang-module-dump.txt @@ -3,6 +3,16 @@ "name": "TopLevel", "printedName": "TopLevel", "children": [ + { + "kind": "Import", + "name": "ObjectiveC", + "printedName": "ObjectiveC", + "declKind": "Import", + "moduleName": "Foo", + "declAttributes": [ + "Exported" + ] + }, { "kind": "TypeDecl", "name": "AnotherObjcProt", diff --git a/test/api-digester/diff-demangled-name.swift b/test/api-digester/diff-demangled-name.swift new file mode 100644 index 0000000000000..3a5d8290508d4 --- /dev/null +++ b/test/api-digester/diff-demangled-name.swift @@ -0,0 +1,21 @@ +// RUN: %empty-directory(%t) +// RUN: echo "public func foo() {}" > %t/Foo.swift + +// RUN: echo "public protocol P { associatedtype A }" > %t/Foo-1.swift +// RUN: echo "public extension P { public func f() where A == Int {} }" >> %t/Foo-1.swift + +// RUN: echo "public protocol P { associatedtype A }" > %t/Foo-2.swift +// RUN: echo "public extension P where A == Int { public func f() {} }" >> %t/Foo-2.swift + +// RUN: %target-swift-frontend -emit-module %t/Foo-1.swift -module-name Foo -emit-module-interface-path %t/Foo1.swiftinterface +// RUN: %target-swift-frontend -emit-module %t/Foo-2.swift -module-name Foo -emit-module-interface-path %t/Foo2.swiftinterface + +// RUN: %target-swift-frontend -compile-module-from-interface %t/Foo1.swiftinterface -o %t/Foo1.swiftmodule -module-name Foo -emit-abi-descriptor-path %t/Foo1.json + +// RUN: %target-swift-frontend -compile-module-from-interface %t/Foo2.swiftinterface -o %t/Foo2.swiftmodule -module-name Foo -emit-abi-descriptor-path %t/Foo2.json + +// RUN: %api-digester -diagnose-sdk -print-module --input-paths %t/Foo1.json -input-paths %t/Foo2.json -abi -o %t/result.txt + +// RUN: %FileCheck %s < %t/result.txt + +// CHECK: Foo: Func P.f() has mangled name changing from '(extension in Foo):Foo.P.f< where A.A == Swift.Int>() -> ()' to '(extension in Foo):Foo.P< where A.A == Swift.Int>.f() -> ()' diff --git a/tools/driver/swift_api_digester_main.cpp b/tools/driver/swift_api_digester_main.cpp index 1e247922a1b87..07ebc0244d06c 100644 --- a/tools/driver/swift_api_digester_main.cpp +++ b/tools/driver/swift_api_digester_main.cpp @@ -292,16 +292,6 @@ struct MatchedNodeListener { virtual ~MatchedNodeListener() = default; }; -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(); -} - static void singleMatch(SDKNode* Left, SDKNode *Right, MatchedNodeListener &Listener) { @@ -400,7 +390,7 @@ class RemovedAddedNodeMatcher : public NodeMatcher, public MatchedNodeListener { void handleUnmatch(NodeVector &Matched, NodeVector &All, bool Left) { for (auto A : All) { - if (contains(Matched, A)) + if (llvm::is_contained(Matched, A)) continue; if (Left) Listener.foundMatch(A, nullptr, NodeMatchReason::Removed); @@ -597,12 +587,12 @@ class RemovedAddedNodeMatcher : public NodeMatcher, public MatchedNodeListener { NodeVector RenameRight; for (auto Remain : Removed) { - if (!contains(RemovedMatched, Remain)) + if (!llvm::is_contained(RemovedMatched, Remain)) RenameLeft.push_back(Remain); } for (auto Remain : Added) { - if (!contains(AddedMatched, Remain)) + if (!llvm::is_contained(AddedMatched, Remain)) RenameRight.push_back(Remain); } @@ -704,7 +694,7 @@ void SameNameNodeMatcher::match() { for (auto *RN : Right) { // If RN has matched before, ignore it. - if (contains(MatchedRight, RN)) + if (llvm::is_contained(MatchedRight, RN)) continue; // If LN and RN have the same name for some reason, keep track of RN. @@ -723,7 +713,7 @@ void SameNameNodeMatcher::match() { } } for (auto &R : Right) { - if (!contains(MatchedRight, R)) { + if (!llvm::is_contained(MatchedRight, R)) { Added.push_back(R); } } @@ -766,305 +756,8 @@ class SDKTreeDiffPass { virtual void pass(NodePtr Left, NodePtr Right) = 0; virtual ~SDKTreeDiffPass() {} }; - -static void detectRename(SDKNode *L, SDKNode *R) { - if (L->getKind() == R->getKind() && isa(L) && - L->getPrintedName() != R->getPrintedName()) { - L->annotate(NodeAnnotation::Rename); - L->annotate(NodeAnnotation::RenameOldName, L->getPrintedName()); - L->annotate(NodeAnnotation::RenameNewName, R->getPrintedName()); - } -} - -static bool isOwnershipEquivalent(ReferenceOwnership Left, - ReferenceOwnership Right) { - if (Left == Right) - return true; - if (Left == ReferenceOwnership::Unowned && Right == ReferenceOwnership::Weak) - return true; - if (Left == ReferenceOwnership::Weak && Right == ReferenceOwnership::Unowned) - return true; - return false; -} }// End of anonymous namespace -void swift::ide::api::SDKNodeDeclType::diagnose(SDKNode *Right) { - SDKNodeDecl::diagnose(Right); - auto *R = dyn_cast(Right); - if (!R) - return; - auto Loc = R->getLoc(); - if (getDeclKind() != R->getDeclKind()) { - emitDiag(Loc, diag::decl_kind_changed, getDeclKindStr(R->getDeclKind(), - getSDKContext().getOpts().CompilerStyle)); - return; - } - - assert(getDeclKind() == R->getDeclKind()); - auto DKind = getDeclKind(); - switch (DKind) { - case DeclKind::Class: { - auto LSuperClass = getSuperClassName(); - auto RSuperClass = R->getSuperClassName(); - if (!LSuperClass.empty() && LSuperClass != RSuperClass) { - if (RSuperClass.empty()) { - emitDiag(Loc, diag::super_class_removed, LSuperClass); - } else if (!contains(R->getClassInheritanceChain(), LSuperClass)) { - emitDiag(Loc, diag::super_class_changed, LSuperClass, RSuperClass); - } - } - - // Check for @_hasMissingDesignatedInitializers and - // @_inheritsConvenienceInitializers changes. - if (isOpen() && R->isOpen()) { - // It's not safe to add new, invisible designated inits to open - // classes. - if (!hasMissingDesignatedInitializers() && - R->hasMissingDesignatedInitializers()) - R->emitDiag(R->getLoc(), diag::added_invisible_designated_init); - } - - // It's not safe to stop inheriting convenience inits, it changes - // the set of initializers that are available. - if (inheritsConvenienceInitializers() && - !R->inheritsConvenienceInitializers()) - R->emitDiag(R->getLoc(), diag::not_inheriting_convenience_inits); - break; - } - default: - break; - } -} - -void swift::ide::api::SDKNodeDeclAbstractFunc::diagnose(SDKNode *Right) { - SDKNodeDecl::diagnose(Right); - auto *R = dyn_cast(Right); - if (!R) - return; - auto Loc = R->getLoc(); - if (!isThrowing() && R->isThrowing()) { - emitDiag(Loc, diag::decl_new_attr, Ctx.buffer("throwing")); - } - if (Ctx.checkingABI()) { - if (reqNewWitnessTableEntry() != R->reqNewWitnessTableEntry()) { - emitDiag(Loc, diag::decl_new_witness_table_entry, reqNewWitnessTableEntry()); - } - } -} - -void swift::ide::api::SDKNodeDeclFunction::diagnose(SDKNode *Right) { - SDKNodeDeclAbstractFunc::diagnose(Right); - auto *R = dyn_cast(Right); - if (!R) - return; - auto Loc = R->getLoc(); - if (getSelfAccessKind() != R->getSelfAccessKind()) { - emitDiag(Loc, diag::func_self_access_change, getSelfAccessKind(), - R->getSelfAccessKind()); - } - if (Ctx.checkingABI()) { - if (hasFixedBinaryOrder() != R->hasFixedBinaryOrder()) { - emitDiag(Loc, diag::func_has_fixed_order_change, hasFixedBinaryOrder()); - } - } -} - -static StringRef getAttrName(DeclAttrKind Kind) { - switch (Kind) { -#define DECL_ATTR(NAME, CLASS, ...) \ - case DAK_##CLASS: \ - return DeclAttribute::isDeclModifier(DAK_##CLASS) ? #NAME : "@"#NAME; -#include "swift/AST/Attr.def" - case DAK_Count: - llvm_unreachable("unrecognized attribute kind."); - } - llvm_unreachable("covered switch"); -} - -static bool shouldDiagnoseAddingAttribute(SDKNodeDecl *D, DeclAttrKind Kind) { - return true; -} - -static bool shouldDiagnoseRemovingAttribute(SDKNodeDecl *D, DeclAttrKind Kind) { - return true; -} - -void swift::ide::api::SDKNodeDecl::diagnose(SDKNode *Right) { - SDKNode::diagnose(Right); - auto *RD = dyn_cast(Right); - if (!RD) - return; - detectRename(this, RD); - auto Loc = RD->getLoc(); - if (isOpen() && !RD->isOpen()) { - emitDiag(Loc, diag::no_longer_open); - } - - // Diagnose static attribute change. - if (isStatic() ^ RD->isStatic()) { - emitDiag(Loc, diag::decl_new_attr, Ctx.buffer(isStatic() ? "not static" : - "static")); - } - - // Diagnose ownership change. - if (!isOwnershipEquivalent(getReferenceOwnership(), - RD->getReferenceOwnership())) { - auto getOwnershipDescription = [&](swift::ReferenceOwnership O) { - if (O == ReferenceOwnership::Strong) - return Ctx.buffer("strong"); - return keywordOf(O); - }; - emitDiag(Loc, diag::decl_attr_change, - getOwnershipDescription(getReferenceOwnership()), - getOwnershipDescription(RD->getReferenceOwnership())); - } - // Diagnose generic signature change - if (getGenericSignature() != RD->getGenericSignature()) { - // Prefer sugared signature in diagnostics to be more user-friendly. - if (Ctx.commonVersionAtLeast(2) && - getSugaredGenericSignature() != RD->getSugaredGenericSignature()) { - emitDiag(Loc, diag::generic_sig_change, - getSugaredGenericSignature(), RD->getSugaredGenericSignature()); - } else { - emitDiag(Loc, diag::generic_sig_change, - getGenericSignature(), RD->getGenericSignature()); - } - } - - // ObjC name changes are considered breakage - if (getObjCName() != RD->getObjCName()) { - if (Ctx.commonVersionAtLeast(4)) { - emitDiag(Loc, diag::objc_name_change, getObjCName(), RD->getObjCName()); - } - } - - if (isOptional() != RD->isOptional()) { - if (Ctx.checkingABI()) { - // Both adding/removing optional is ABI-breaking. - emitDiag(Loc, diag::optional_req_changed, isOptional()); - } else if (isOptional()) { - // Removing optional is source-breaking. - emitDiag(Loc, diag::optional_req_changed, isOptional()); - } - } - - // Diagnose removing attributes. - for (auto Kind: getDeclAttributes()) { - if (!RD->hasDeclAttribute(Kind)) { - if ((Ctx.checkingABI() ? DeclAttribute::isRemovingBreakingABI(Kind) : - DeclAttribute::isRemovingBreakingAPI(Kind)) && - shouldDiagnoseRemovingAttribute(this, Kind)) { - emitDiag(Loc, diag::decl_new_attr, - Ctx.buffer((llvm::Twine("without ") + getAttrName(Kind)).str())); - } - } - } - - // Diagnose adding attributes. - for (auto Kind: RD->getDeclAttributes()) { - if (!hasDeclAttribute(Kind)) { - if ((Ctx.checkingABI() ? DeclAttribute::isAddingBreakingABI(Kind) : - DeclAttribute::isAddingBreakingAPI(Kind)) && - shouldDiagnoseAddingAttribute(this, Kind)) { - emitDiag(Loc, diag::decl_new_attr, - Ctx.buffer((llvm::Twine("with ") + getAttrName(Kind)).str())); - } - } - } - - if (Ctx.checkingABI()) { - if (hasFixedBinaryOrder() && RD->hasFixedBinaryOrder() && - getFixedBinaryOrder() != RD->getFixedBinaryOrder()) { - emitDiag(Loc, diag::decl_reorder, getFixedBinaryOrder(), - RD->getFixedBinaryOrder()); - } - } -} - -void swift::ide::api::SDKNodeDeclOperator::diagnose(SDKNode *Right) { - SDKNodeDecl::diagnose(Right); - auto *RO = dyn_cast(Right); - if (!RO) - return; - auto Loc = RO->getLoc(); - if (getDeclKind() != RO->getDeclKind()) { - emitDiag(Loc, diag::decl_kind_changed, getDeclKindStr(RO->getDeclKind(), - getSDKContext().getOpts().CompilerStyle)); - } -} - -void swift::ide::api::SDKNodeDeclVar::diagnose(SDKNode *Right) { - SDKNodeDecl::diagnose(Right); - auto *RV = dyn_cast(Right); - if (!RV) - return; - auto Loc = RV->getLoc(); - if (Ctx.checkingABI()) { - if (hasFixedBinaryOrder() != RV->hasFixedBinaryOrder()) { - emitDiag(Loc, diag::var_has_fixed_order_change, hasFixedBinaryOrder()); - } - } -} - -static bool shouldDiagnoseType(SDKNodeType *T) { - return T->isTopLevelType(); -} - -void swift::ide::api::SDKNodeType::diagnose(SDKNode *Right) { - SDKNode::diagnose(Right); - auto *RT = dyn_cast(Right); - if (!RT || !shouldDiagnoseType(this)) - return; - assert(isTopLevelType()); - - // Diagnose type witness changes when diagnosing ABI breakages. - if (auto *Wit = dyn_cast(getParent())) { - auto *Conform = Wit->getParent()->getAs(); - if (Ctx.checkingABI() && getPrintedName() != RT->getPrintedName()) { - auto *LD = Conform->getNominalTypeDecl(); - LD->emitDiag(SourceLoc(), diag::type_witness_change, - Wit->getWitnessedTypeName(), - getPrintedName(), RT->getPrintedName()); - } - return; - } - - StringRef Descriptor = getTypeRoleDescription(); - assert(isa(getParent())); - auto LParent = cast(getParent()); - assert(LParent->getKind() == RT->getParent()->getAs()->getKind()); - auto Loc = RT->getParent()->getAs()->getLoc(); - if (getPrintedName() != RT->getPrintedName()) { - LParent->emitDiag(Loc, diag::decl_type_change, - Descriptor, getPrintedName(), RT->getPrintedName()); - } - - if (hasDefaultArgument() && !RT->hasDefaultArgument()) { - LParent->emitDiag(Loc, diag::default_arg_removed, Descriptor); - } - if (getParamValueOwnership() != RT->getParamValueOwnership()) { - LParent->emitDiag(Loc, diag::param_ownership_change, - getTypeRoleDescription(), - getParamValueOwnership(), - RT->getParamValueOwnership()); - } -} - -void swift::ide::api::SDKNodeTypeFunc::diagnose(SDKNode *Right) { - SDKNodeType::diagnose(Right); - auto *RT = dyn_cast(Right); - if (!RT || !shouldDiagnoseType(this)) - return; - assert(isTopLevelType()); - auto Loc = RT->getParent()->getAs()->getLoc(); - if (Ctx.checkingABI() && isEscaping() != RT->isEscaping()) { - getParent()->getAs()->emitDiag(Loc, - diag::func_type_escaping_changed, - getTypeRoleDescription(), - isEscaping()); - } -} - namespace { static void diagnoseRemovedDecl(const SDKNodeDecl *D) { if (D->getSDKContext().checkingABI()) { @@ -1317,6 +1010,7 @@ class PrunePass : public MatchedNodeListener, public SDKTreeDiffPass { case SDKNodeKind::DeclAccessor: case SDKNodeKind::DeclConstructor: case SDKNodeKind::DeclTypeAlias: + case SDKNodeKind::DeclImport: case SDKNodeKind::TypeFunc: case SDKNodeKind::TypeNominal: case SDKNodeKind::TypeAlias: { @@ -2257,7 +1951,7 @@ class RenameDetectorForMemberDiff : public MatchedNodeListener { void foundMatch(NodePtr Left, NodePtr Right, NodeMatchReason Reason) override { if (!Left || !Right) return; - detectRename(Left, Right); + swift::ide::api::detectRename(Left, Right); LeftDetector.detect(Left, Right); RightDetector.detect(Right, Left); }