From 026fcd24feb19a45ba8863a8b56317f3cf2f19f6 Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Thu, 23 May 2024 11:13:44 -0700 Subject: [PATCH] [ScanDependency][canImport] Improve canImport handling in explicit build Teach dependency scanner to report all the module canImport check result to swift-frontend, so swift-frontend doesn't need to parse swiftmodule or parse TBD file to determine the versions. This ensures dependency scanner and swift-frontend will have the same resolution for all canImport checks. This also fixes two related issues: * Previously, in order to get consistant results between scanner and frontend, scanner will request building the module in canImport check even it is not imported later. This slightly alters the definition of the canImport to only succeed when the module can be found AND be built. This also can affect the auto-link in such cases. * For caching build, the location of the clang module is abstracted away so swift-frontend cannot locate the TBD file to resolve underlyingVersion. rdar://128067152 --- include/swift/AST/ASTContext.h | 28 +++- include/swift/AST/DiagnosticsFrontend.def | 2 + include/swift/AST/SearchPathOptions.h | 9 ++ include/swift/Option/FrontendOptions.td | 8 + lib/AST/ASTContext.cpp | 84 ++++++++-- lib/ClangImporter/ClangImporter.cpp | 9 +- .../ModuleDependencyScanner.cpp | 28 +++- lib/Frontend/CompilerInvocation.cpp | 16 ++ test/CAS/can-import.swift | 71 ++++++++- .../can_import_options.swift | 145 ++++++++++++++++++ .../module_deps_can_import.swift | 28 +++- 11 files changed, 381 insertions(+), 47 deletions(-) create mode 100644 test/Parse/ConditionalCompilation/can_import_options.swift diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 4e60303ec39c2..a435ceff672bc 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -47,6 +47,7 @@ #include "llvm/ADT/TinyPtrVector.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/DataTypes.h" +#include "llvm/Support/VersionTuple.h" #include "llvm/Support/VirtualOutputBackend.h" #include #include @@ -409,9 +410,17 @@ class ASTContext final { /// Cache of module names that fail the 'canImport' test in this context. mutable llvm::StringSet<> FailedModuleImportNames; + /// Versions of the modules found during versioned canImport checks. + struct ImportedModuleVersionInfo { + llvm::VersionTuple Version; + llvm::VersionTuple UnderlyingVersion; + }; + /// Cache of module names that passed the 'canImport' test. This cannot be - /// mutable since it needs to be queried for dependency discovery. - llvm::StringSet<> SucceededModuleImportNames; + /// mutable since it needs to be queried for dependency discovery. Keep sorted + /// so caller of `forEachCanImportVersionCheck` can expect deterministic + /// ordering. + std::map CanImportModuleVersions; /// Set if a `-module-alias` was passed. Used to store mapping between module aliases and /// their corresponding real names, and vice versa for a reverse lookup, which is needed to check @@ -1102,7 +1111,12 @@ class ASTContext final { /// module is loaded in full. bool canImportModuleImpl(ImportPath::Module ModulePath, llvm::VersionTuple version, bool underlyingVersion, - bool updateFailingList) const; + bool updateFailingList, + llvm::VersionTuple &foundVersion) const; + + /// Add successful canImport modules. + void addSucceededCanImportModule(StringRef moduleName, bool underlyingVersion, + const llvm::VersionTuple &versionInfo); public: namelookup::ImportCache &getImportCache() const; @@ -1144,10 +1158,10 @@ class ASTContext final { llvm::VersionTuple version = llvm::VersionTuple(), bool underlyingVersion = false) const; - /// \returns a set of names from all successfully canImport module checks. - const llvm::StringSet<> &getSuccessfulCanImportCheckNames() const { - return SucceededModuleImportNames; - } + /// Callback on each successful imported. + void forEachCanImportVersionCheck( + std::function) const; /// \returns a module with a given name that was already loaded. If the /// module was not loaded, returns nullptr. diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index 01125333b40e1..d1eaf7e6096da 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -481,6 +481,8 @@ ERROR(unknown_forced_module_loading_mode,none, (StringRef)) ERROR(error_creating_remark_serializer,none, "error while creating remark serializer: '%0'", (StringRef)) +ERROR(invalid_can_import_module_version,none, + "invalid version passed to -module-can-import-version: '%0'", (StringRef)) REMARK(interface_file_lock_failure,none, "building module interface without lock file", ()) diff --git a/include/swift/AST/SearchPathOptions.h b/include/swift/AST/SearchPathOptions.h index c26a381b96bf1..f58abedcd2e4b 100644 --- a/include/swift/AST/SearchPathOptions.h +++ b/include/swift/AST/SearchPathOptions.h @@ -20,6 +20,7 @@ #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/Error.h" +#include "llvm/Support/VersionTuple.h" #include "llvm/Support/VirtualFileSystem.h" #include @@ -509,6 +510,14 @@ class SearchPathOptions { using CrossImportMap = llvm::StringMap>; CrossImportMap CrossImportInfo; + /// CanImport information passed from scanning. + struct CanImportInfo { + std::string ModuleName; + llvm::VersionTuple Version; + llvm::VersionTuple UnderlyingVersion; + }; + std::vector CanImportModuleInfo; + /// Whether to search for cross import overlay on file system. bool DisableCrossImportOverlaySearch = false; diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 16081dee45216..a50cf5cd7c6ad 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -224,6 +224,14 @@ def swift_module_cross_import: MultiArg<["-"], "swift-module-cross-import", 2>, MetaVarName<" ">, HelpText<"Specify the cross import module">; +def module_can_import: Separate<["-"], "module-can-import">, + MetaVarName<"">, + HelpText<"Specify canImport module name">; + +def module_can_import_version: MultiArg<["-"], "module-can-import-version", 3>, + MetaVarName<" ">, + HelpText<"Specify canImport module and versions">; + def disable_cross_import_overlay_search: Flag<["-"], "disable-cross-import-overlay-search">, HelpText<"Disable searching for cross import overlay file">; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 577297363385c..43156febacf8c 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -74,6 +74,7 @@ #include "llvm/Support/Allocator.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/VersionTuple.h" #include "llvm/Support/VirtualOutputBackend.h" #include "llvm/Support/VirtualOutputBackends.h" #include @@ -786,6 +787,12 @@ ASTContext::ASTContext( registerAccessRequestFunctions(evaluator); registerNameLookupRequestFunctions(evaluator); + // Register canImport module info. + for (auto &info: SearchPathOpts.CanImportModuleInfo) { + addSucceededCanImportModule(info.ModuleName, false, info.Version); + addSucceededCanImportModule(info.ModuleName, true, info.UnderlyingVersion); + } + // Provide a default OnDiskOutputBackend if user didn't supply one. if (!OutputBackend) OutputBackend = llvm::makeIntrusiveRefCnt(); @@ -2385,23 +2392,56 @@ getModuleVersionKindString(const ModuleLoader::ModuleVersionInfo &info) { } } -bool ASTContext::canImportModuleImpl(ImportPath::Module ModuleName, - llvm::VersionTuple version, - bool underlyingVersion, - bool updateFailingList) const { +void ASTContext::addSucceededCanImportModule( + StringRef moduleName, bool underlyingVersion, + const llvm::VersionTuple &versionInfo) { + auto &entry = CanImportModuleVersions[moduleName.str()]; + if (!versionInfo.empty()) { + if (underlyingVersion) + entry.UnderlyingVersion = versionInfo; + else + entry.Version = versionInfo; + } +} + +bool ASTContext::canImportModuleImpl( + ImportPath::Module ModuleName, llvm::VersionTuple version, + bool underlyingVersion, bool updateFailingList, + llvm::VersionTuple &foundVersion) const { SmallString<64> FullModuleName; ModuleName.getString(FullModuleName); - auto ModuleNameStr = FullModuleName.str(); + auto ModuleNameStr = FullModuleName.str().str(); // If we've failed loading this module before, don't look for it again. if (FailedModuleImportNames.count(ModuleNameStr)) return false; - if (version.empty()) { - // If this module has already been checked successfully, it is importable. - if (SucceededModuleImportNames.count(ModuleNameStr)) + // If this module has already been checked or there is information for the + // module from commandline, use that information instead of loading the + // module. + auto Found = CanImportModuleVersions.find(ModuleNameStr); + if (Found != CanImportModuleVersions.end()) { + if (version.empty()) return true; + if (underlyingVersion) { + if (!Found->second.UnderlyingVersion.empty()) + return version <= Found->second.UnderlyingVersion; + } else { + if (!Found->second.Version.empty()) + return version <= Found->second.Version; + } + + // If the canImport information is coming from the command-line, then no + // need to continue the search, return false. For checking modules that are + // not passed from command-line, allow fallback to the module loading since + // this is not in a canImport request context that has already been resolved + // by scanner. + if (!SearchPathOpts.CanImportModuleInfo.empty()) + return false; + } + + if (version.empty()) { // If this module has already been successfully imported, it is importable. if (getLoadedModule(ModuleName) != nullptr) return true; @@ -2453,28 +2493,38 @@ bool ASTContext::canImportModuleImpl(ImportPath::Module ModuleName, return true; } + foundVersion = bestVersionInfo.getVersion(); return version <= bestVersionInfo.getVersion(); } -bool ASTContext::canImportModule(ImportPath::Module ModuleName, +void ASTContext::forEachCanImportVersionCheck( + std::function + Callback) const { + for (auto &entry : CanImportModuleVersions) + Callback(entry.first, entry.second.Version, entry.second.UnderlyingVersion); +} + +bool ASTContext::canImportModule(ImportPath::Module moduleName, llvm::VersionTuple version, bool underlyingVersion) { - if (!canImportModuleImpl(ModuleName, version, underlyingVersion, true)) + llvm::VersionTuple versionInfo; + if (!canImportModuleImpl(moduleName, version, underlyingVersion, true, + versionInfo)) return false; - // If checked successfully, add the top level name to success list as - // dependency to handle clang submodule correctly. Swift does not have - // submodule so the name should be the same. - SmallString<64> TopModuleName; - ModuleName.getTopLevelPath().getString(TopModuleName); - SucceededModuleImportNames.insert(TopModuleName.str()); + SmallString<64> fullModuleName; + moduleName.getString(fullModuleName); + addSucceededCanImportModule(fullModuleName, underlyingVersion, versionInfo); return true; } bool ASTContext::testImportModule(ImportPath::Module ModuleName, llvm::VersionTuple version, bool underlyingVersion) const { - return canImportModuleImpl(ModuleName, version, underlyingVersion, false); + llvm::VersionTuple versionInfo; + return canImportModuleImpl(ModuleName, version, underlyingVersion, false, + versionInfo); } ModuleDecl * diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 5ebe4908e2bdd..7be37d3953d38 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -2120,7 +2120,8 @@ bool ClangImporter::isModuleImported(const clang::Module *M) { return M->NameVisibility == clang::Module::NameVisibilityKind::AllVisible; } -static llvm::VersionTuple getCurrentVersionFromTBD(StringRef path, +static llvm::VersionTuple getCurrentVersionFromTBD(llvm::vfs::FileSystem &FS, + StringRef path, StringRef moduleName) { std::string fwName = (moduleName + ".framework").str(); auto pos = path.find(fwName); @@ -2130,7 +2131,7 @@ static llvm::VersionTuple getCurrentVersionFromTBD(StringRef path, llvm::sys::path::append(buffer, moduleName + ".tbd"); auto tbdPath = buffer.str(); llvm::ErrorOr> tbdBufOrErr = - llvm::MemoryBuffer::getFile(tbdPath); + FS.getBufferForFile(tbdPath); // .tbd file doesn't exist, exit. if (!tbdBufOrErr) return {}; @@ -2193,8 +2194,8 @@ bool ClangImporter::canImportModule(ImportPath::Module modulePath, // Look for the .tbd file inside .framework dir to get the project version // number. - llvm::VersionTuple currentVersion = - getCurrentVersionFromTBD(path, topModule.Item.str()); + llvm::VersionTuple currentVersion = getCurrentVersionFromTBD( + Impl.Instance->getVirtualFileSystem(), path, topModule.Item.str()); versionInfo->setVersion(currentVersion, ModuleVersionSourceKind::ClangModuleTBD); return true; diff --git a/lib/DependencyScan/ModuleDependencyScanner.cpp b/lib/DependencyScan/ModuleDependencyScanner.cpp index 899fe59c5c7bd..636b2522e16ee 100644 --- a/lib/DependencyScan/ModuleDependencyScanner.cpp +++ b/lib/DependencyScan/ModuleDependencyScanner.cpp @@ -31,6 +31,7 @@ #include "llvm/CAS/CachingOnDiskFileSystem.h" #include "llvm/Support/Error.h" #include "llvm/Support/Threading.h" +#include "llvm/Support/VersionTuple.h" #include "llvm/Support/VirtualFileSystem.h" #include @@ -464,15 +465,26 @@ ModuleDependencyScanner::getMainModuleDependencyInfo(ModuleDecl *mainModule) { &ScanASTContext.SourceMgr); } - // Add all the successful canImport checks from the ASTContext as part of - // the dependency since only mainModule can have `canImport` check. This - // needs to happen after visiting all the top-level decls from all + // Pass all the successful canImport checks from the ASTContext as part of + // build command to main module to ensure frontend gets the same result. + // This needs to happen after visiting all the top-level decls from all // SourceFiles. - for (auto &Module : - mainModule->getASTContext().getSuccessfulCanImportCheckNames()) - mainDependencies.addModuleImport(Module.first(), - &alreadyAddedModules); - } + auto buildArgs = mainDependencies.getCommandline(); + mainModule->getASTContext().forEachCanImportVersionCheck( + [&](StringRef moduleName, const llvm::VersionTuple &Version, + const llvm::VersionTuple &UnderlyingVersion) { + if (Version.empty() && UnderlyingVersion.empty()) { + buildArgs.push_back("-module-can-import"); + buildArgs.push_back(moduleName.str()); + } else { + buildArgs.push_back("-module-can-import-version"); + buildArgs.push_back(moduleName.str()); + buildArgs.push_back(Version.getAsString()); + buildArgs.push_back(UnderlyingVersion.getAsString()); + } + }); + mainDependencies.updateCommandLine(buildArgs); + } return mainDependencies; } diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 671868399bccb..836a0958399ca 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -25,6 +25,7 @@ #include "swift/Strings.h" #include "swift/SymbolGraphGen/SymbolGraphOptions.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/Support/VersionTuple.h" #include "llvm/TargetParser/Triple.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" @@ -2144,6 +2145,21 @@ static bool ParseSearchPathArgs(SearchPathOptions &Opts, ArgList &Args, for (auto *A : Args.filtered(OPT_swift_module_cross_import)) Opts.CrossImportInfo[A->getValue(0)].push_back(A->getValue(1)); + for (auto &Name : Args.getAllArgValues(OPT_module_can_import)) + Opts.CanImportModuleInfo.push_back({Name, {}, {}}); + + for (auto *A: Args.filtered(OPT_module_can_import_version)) { + llvm::VersionTuple Version, UnderlyingVersion; + if (Version.tryParse(A->getValue(1))) + Diags.diagnose(SourceLoc(), diag::invalid_can_import_module_version, + A->getValue(1)); + if (UnderlyingVersion.tryParse(A->getValue(2))) + Diags.diagnose(SourceLoc(), diag::invalid_can_import_module_version, + A->getValue(2)); + Opts.CanImportModuleInfo.push_back( + {A->getValue(0), Version, UnderlyingVersion}); + } + Opts.DisableCrossImportOverlaySearch |= Args.hasArg(OPT_disable_cross_import_overlay_search); diff --git a/test/CAS/can-import.swift b/test/CAS/can-import.swift index 8483483cf0a3b..efcd420435c8f 100644 --- a/test/CAS/can-import.swift +++ b/test/CAS/can-import.swift @@ -3,13 +3,7 @@ // RUN: %target-swift-frontend -scan-dependencies -module-name Test -module-cache-path %t/clang-module-cache -O \ // RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import -parse-stdlib \ -// RUN: %t/main.swift -o %t/deps.json -swift-version 5 -cache-compile-job -cas-path %t/cas -I %t/include - -// RUN: %{python} %S/Inputs/BuildCommandExtractor.py %t/deps.json clang:A > %t/A.cmd -// RUN: %swift_frontend_plain @%t/A.cmd - -// RUN: %{python} %S/Inputs/BuildCommandExtractor.py %t/deps.json clang:B > %t/B.cmd -// RUN: %swift_frontend_plain @%t/B.cmd +// RUN: %t/main.swift -o %t/deps.json -swift-version 5 -cache-compile-job -cas-path %t/cas -I %t/include -F %t/frameworks // RUN: %{python} %S/Inputs/BuildCommandExtractor.py %t/deps.json C > %t/C.cmd // RUN: %swift_frontend_plain @%t/C.cmd @@ -18,6 +12,22 @@ // RUN: llvm-cas --cas %t/cas --make-blob --data %t/map.json > %t/map.casid // RUN: %{python} %S/Inputs/BuildCommandExtractor.py %t/deps.json Test > %t/MyApp.cmd +// RUN: %FileCheck %s --check-prefix=CMD --input-file=%t/MyApp.cmd +// CMD: "-module-can-import" +// CMD-NEXT: "A.Sub" +// CMD-NEXT: "-module-can-import" +// CMD-NEXT: "B" +// CMD-NEXT: "-module-can-import-version" +// CMD-NEXT: "C" +// CMD-NEXT: "1.0" +// CMD-NEXT: "0" +// CMD-NEXT: "-module-can-import" +// CMD-NEXT: "D" +// CMD-NEXT: "-module-can-import-version" +// CMD-NEXT: "Simple" +// CMD-NEXT: "0" +// CMD-NEXT: "1000.0.0" + // RUN: %target-swift-frontend \ // RUN: -typecheck -cache-compile-job -cas-path %t/cas \ // RUN: -swift-version 5 -disable-implicit-swift-modules \ @@ -38,6 +48,7 @@ import A.Missing func b() {} #endif +// check version. #if canImport(C, _version: 1.0) import C #endif @@ -46,10 +57,29 @@ import C import Missing #endif +// check succeeded canImport followed by unsuccessful versioned canImport check. +#if canImport(D) +func imported() {} +#endif + +#if canImport(D, _version: 2.0) +import Missing +#endif + +// check underlyingVersion. +#if canImport(Simple, _underlyingVersion: 10) +func simple() {} +#endif + +#if canImport(Simple, _underlyingVersion: 2000) +#error("wrong version") +#endif + func useA() { a() b() c() + simple() } //--- include/module.modulemap @@ -75,3 +105,30 @@ void notused2(void); // swift-interface-format-version: 1.0 // swift-module-flags: -module-name C -O -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import -parse-stdlib -user-module-version 1.0 public func c() { } + +//--- include/D.swiftinterface +// swift-interface-format-version: 1.0 +// swift-module-flags: -module-name D -O -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import -parse-stdlib -user-module-version 1.0 +public func d() { } + +//--- frameworks/Simple.framework/Modules/module.modulemap +framework module Simple { + umbrella header "Simple.h" + export * + module * { + export * + } +} + +//--- frameworks/Simple.framework/Headers/Simple.h +void simple(void); + +//--- frameworks/Simple.framework/Simple.tbd +--- !tapi-tbd +tbd-version: 4 +targets: [ arm64-macos, arm64-ios, arm64-watchos, arm64-tvos, + arm64-ios-simulator, arm64-watchos-simulator, arm64-tvos-simulator ] +flags: [ not_app_extension_safe ] +install-name: '/System/Library/Frameworks/Simple.framework/Versions/A/Simple' +current-version: 1000 +... diff --git a/test/Parse/ConditionalCompilation/can_import_options.swift b/test/Parse/ConditionalCompilation/can_import_options.swift new file mode 100644 index 0000000000000..65533085997f6 --- /dev/null +++ b/test/Parse/ConditionalCompilation/can_import_options.swift @@ -0,0 +1,145 @@ +// RUN: %empty-directory(%t) +// RUN: %target-typecheck-verify-swift -disable-implicit-concurrency-module-import \ +// RUN: -disable-implicit-string-processing-module-import \ +// RUN: -module-can-import Foo -module-can-import-version Bar 113.330.1.2 0.0 \ +// RUN: -module-can-import-version Baz 113.330.1.2 113.330.1.2 + +func canImport() { +#if canImport(Foo) + let basicCheck = 1 // expected-warning {{initialization of immutable value 'basicCheck' was never used; consider replacing with assignment to '_' or removing it}} +#endif + +#if canImport(Foo, _version: 1) + // No actual Foo to be imported since it is not versioned. + let versionCheck = 1 +#endif +} + +func canImportVersioned() { +#if canImport(Bar, _version: 0) + let majorZero = 1 // expected-warning {{initialization of immutable value 'majorZero' was never used; consider replacing with assignment to '_' or removing it}} +#endif + +#if canImport(Bar, _version: 112) + let majorSmaller = 1 // expected-warning {{initialization of immutable value 'majorSmaller' was never used; consider replacing with assignment to '_' or removing it}} +#endif + +#if canImport(Bar, _version: 113) + let majorEqual = 1 // expected-warning {{initialization of immutable value 'majorEqual' was never used; consider replacing with assignment to '_' or removing it}} +#endif + +#if canImport(Bar, _version: 114) + let majorLarger = 1 +#endif + +#if canImport(Bar, _version: 113.329) + let minorSmaller = 1 // expected-warning {{initialization of immutable value 'minorSmaller' was never used; consider replacing with assignment to '_' or removing it}} +#endif + +#if canImport(Bar, _version: 113.330) + let minorEqual = 1 // expected-warning {{initialization of immutable value 'minorEqual' was never used; consider replacing with assignment to '_' or removing it}} +#endif + +#if canImport(Bar, _version: 113.331) + let minorLarger = 1 +#endif + +#if canImport(Bar, _version: 113.330.0) + let patchSmaller = 1 // expected-warning {{initialization of immutable value 'patchSmaller' was never used; consider replacing with assignment to '_' or removing it}} +#endif + +#if canImport(Bar, _version: 113.330.1) + let patchEqual = 1 // expected-warning {{initialization of immutable value 'patchEqual' was never used; consider replacing with assignment to '_' or removing it}} +#endif + +#if canImport(Bar, _version: 113.330.2) + let patchLarger = 1 +#endif + +#if canImport(Bar, _version: 113.330.1.1) + let buildSmaller = 1 // expected-warning {{initialization of immutable value 'buildSmaller' was never used; consider replacing with assignment to '_' or removing it}} +#endif + +#if canImport(Bar, _version: 113.330.1.2) + let buildEqual = 1 // expected-warning {{initialization of immutable value 'buildEqual' was never used; consider replacing with assignment to '_' or removing it}} +#endif + +#if canImport(Bar, _version: 113.330.1.3) + let buildLarger = 1 +#endif + +#if canImport(Bar, _version: 113.330.1.2.0) // expected-warning {{trailing components of version '113.330.1.2' are ignored}} + let extraComponent = 1 // expected-warning {{initialization of immutable value 'extraComponent' was never used; consider replacing with assignment to '_' or removing it}} +#endif + +#if canImport(Bar, _underlyingVersion: 113.33) + // Bar is a Swift module with no underlying clang module. + let underlyingMinorSmaller = 1 +#endif + +#if canImport(Bar) + let noVersion = 1 // expected-warning {{initialization of immutable value 'noVersion' was never used; consider replacing with assignment to '_' or removing it}} +#endif +} + +func canImportUnderlyingVersion() { +#if canImport(Baz, _underlyingVersion: 0) + let majorZero = 1 // expected-warning {{initialization of immutable value 'majorZero' was never used; consider replacing with assignment to '_' or removing it}} +#endif + +#if canImport(Baz, _underlyingVersion: 112) + let majorSmaller = 1 // expected-warning {{initialization of immutable value 'majorSmaller' was never used; consider replacing with assignment to '_' or removing it}} +#endif + +#if canImport(Baz, _underlyingVersion: 113) + let majorEqual = 1 // expected-warning {{initialization of immutable value 'majorEqual' was never used; consider replacing with assignment to '_' or removing it}} +#endif + +#if canImport(Baz, _underlyingVersion: 114) + let majorLarger = 1 +#endif + +#if canImport(Baz, _underlyingVersion: 113.329) + let minorSmaller = 1 // expected-warning {{initialization of immutable value 'minorSmaller' was never used; consider replacing with assignment to '_' or removing it}} +#endif + +#if canImport(Baz, _underlyingVersion: 113.330) + let minorEqual = 1 // expected-warning {{initialization of immutable value 'minorEqual' was never used; consider replacing with assignment to '_' or removing it}} +#endif + +#if canImport(Baz, _underlyingVersion: 113.331) + let minorLarger = 1 +#endif + +#if canImport(Baz, _underlyingVersion: 113.330.0) + let patchSmaller = 1 // expected-warning {{initialization of immutable value 'patchSmaller' was never used; consider replacing with assignment to '_' or removing it}} +#endif + +#if canImport(Baz, _underlyingVersion: 113.330.1) + let patchEqual = 1 // expected-warning {{initialization of immutable value 'patchEqual' was never used; consider replacing with assignment to '_' or removing it}} +#endif + +#if canImport(Baz, _underlyingVersion: 113.330.2) + let patchLarger = 1 +#endif + +#if canImport(Baz, _underlyingVersion: 113.330.1.1) + let buildSmaller = 1 // expected-warning {{initialization of immutable value 'buildSmaller' was never used; consider replacing with assignment to '_' or removing it}} +#endif + +#if canImport(Baz, _underlyingVersion: 113.330.1.2) + let buildEqual = 1 // expected-warning {{initialization of immutable value 'buildEqual' was never used; consider replacing with assignment to '_' or removing it}} +#endif + +#if canImport(Baz, _underlyingVersion: 113.330.1.3) + let buildLarger = 1 +#endif + +#if canImport(Baz, _underlyingVersion: 113.330.1.2.0) // expected-warning {{trailing components of version '113.330.1.2' are ignored}} + let extraComponent = 1 // expected-warning {{initialization of immutable value 'extraComponent' was never used; consider replacing with assignment to '_' or removing it}} +#endif + +#if canImport(Baz, _version: 113.33) + let version = 1 // expected-warning {{initialization of immutable value 'version' was never used; consider replacing with assignment to '_' or removing it}} +#endif +} diff --git a/test/ScanDependencies/module_deps_can_import.swift b/test/ScanDependencies/module_deps_can_import.swift index 2ec33713d3301..64f503e77a4fa 100644 --- a/test/ScanDependencies/module_deps_can_import.swift +++ b/test/ScanDependencies/module_deps_can_import.swift @@ -4,14 +4,24 @@ // RUN: %{python} %S/../CAS/Inputs/SwiftDepsExtractor.py %t/deps.json Test directDependencies | %FileCheck %s -// CHECK-DAG: "clang": "C" -// CHECK-DAG: "clang": "ClangTest" // CHECK-DAG: "swift": "A" -// CHECK-DAG: "swift": "F" +// CHECK-DAG: "clang": "ClangTest" // CHECK-NOT: "swift": "G" // CHECK-NOT: "swift": "B" // CHECK-NOT: "Missing" +// RUN: %{python} %S/../CAS/Inputs/BuildCommandExtractor.py %t/deps.json Test | %FileCheck %s -check-prefix=CMD +// CMD: "-module-can-import" +// CMD-NEXT: "C" +// CMD-NEXT: "-module-can-import" +// CMD-NEXT: "ClangTest.Sub" +// CMD-NEXT: "-module-can-import" +// CMD-NEXT: "F" +// CMD-NEXT: "-module-can-import-version" +// CMD-NEXT: "Version" +// CMD-NEXT: "100.1" +// CMD-NEXT: "0" + //--- main.swift #if canImport(Missing) @@ -35,10 +45,16 @@ import Missing // B is short circuited #if canImport(C) || canImport(B) +import B #endif -// Check clang submodule, this should import ClangTest, not ClangTest.Sub +// Check clang submodule #if canImport(ClangTest.Sub) +import ClangTest.Sub +#endif + +// Versioned check +#if canImport(Version, _version: 10.0) #endif //--- include/module.modulemap @@ -51,3 +67,7 @@ module ClangTest { //--- include/sub.h void notused(void); + +//--- include/Version.swiftinterface +// swift-interface-format-version: 1.0 +// swift-module-flags: -module-name Version -O -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import -parse-stdlib -user-module-version 100.1