Skip to content

Commit 1698d25

Browse files
authored
Merge pull request #37249 from nkcsgexi/73992299-5
ClangImporter: check version specified in canImport with the project version specified in .tbd files
2 parents 38497aa + a40f22e commit 1698d25

File tree

3 files changed

+126
-1
lines changed

3 files changed

+126
-1
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@
6565
#include "llvm/Support/FileCollector.h"
6666
#include "llvm/Support/Memory.h"
6767
#include "llvm/Support/Path.h"
68+
#include "llvm/Support/YAMLTraits.h"
69+
#include "llvm/Support/YAMLParser.h"
6870
#include <algorithm>
6971
#include <memory>
7072

@@ -1731,6 +1733,11 @@ bool ClangImporter::isModuleImported(const clang::Module *M) {
17311733
return M->NameVisibility == clang::Module::NameVisibilityKind::AllVisible;
17321734
}
17331735

1736+
static std::string getScalaNodeText(llvm::yaml::Node *N) {
1737+
SmallString<32> Buffer;
1738+
return cast<llvm::yaml::ScalarNode>(N)->getValue(Buffer).str();
1739+
}
1740+
17341741
bool ClangImporter::canImportModule(ImportPath::Element moduleID,
17351742
llvm::VersionTuple version,
17361743
bool underlyingVersion) {
@@ -1748,7 +1755,66 @@ bool ClangImporter::canImportModule(ImportPath::Element moduleID,
17481755
clang::Module::UnresolvedHeaderDirective mh;
17491756
clang::Module *m;
17501757
auto &ctx = Impl.getClangASTContext();
1751-
return clangModule->isAvailable(ctx.getLangOpts(), getTargetInfo(), r, mh, m);
1758+
auto available = clangModule->isAvailable(ctx.getLangOpts(), getTargetInfo(),
1759+
r, mh, m);
1760+
if (!available)
1761+
return false;
1762+
if (version.empty())
1763+
return true;
1764+
assert(available);
1765+
assert(!version.empty());
1766+
llvm::VersionTuple currentVersion;
1767+
StringRef path = getClangASTContext().getSourceManager()
1768+
.getFilename(clangModule->DefinitionLoc);
1769+
// Look for the .tbd file inside .framework dir to get the project version
1770+
// number.
1771+
std::string fwName = (llvm::Twine(moduleID.Item.str()) + ".framework").str();
1772+
auto pos = path.find(fwName);
1773+
while (pos != StringRef::npos) {
1774+
llvm::SmallString<256> buffer(path.substr(0, pos + fwName.size()));
1775+
llvm::sys::path::append(buffer, llvm::Twine(moduleID.Item.str()) + ".tbd");
1776+
auto tbdPath = buffer.str();
1777+
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> tbdBufOrErr =
1778+
llvm::MemoryBuffer::getFile(tbdPath);
1779+
// .tbd file doesn't exist, break.
1780+
if (!tbdBufOrErr) {
1781+
break;
1782+
}
1783+
StringRef tbdBuffer = tbdBufOrErr->get()->getBuffer();
1784+
1785+
// Use a new source manager instead of the one from ASTContext because we
1786+
// don't want the Json file to be persistent.
1787+
SourceManager SM;
1788+
llvm::yaml::Stream Stream(llvm::MemoryBufferRef(tbdBuffer, tbdPath),
1789+
SM.getLLVMSourceMgr());
1790+
auto DI = Stream.begin();
1791+
assert(DI != Stream.end() && "Failed to read a document");
1792+
llvm::yaml::Node *N = DI->getRoot();
1793+
assert(N && "Failed to find a root");
1794+
auto *pairs = dyn_cast_or_null<llvm::yaml::MappingNode>(N);
1795+
if (!pairs)
1796+
break;
1797+
for (auto &keyValue: *pairs) {
1798+
auto key = getScalaNodeText(keyValue.getKey());
1799+
// Look for field "current-version" in the .tbd file.
1800+
if (key == "current-version") {
1801+
auto ver = getScalaNodeText(keyValue.getValue());
1802+
currentVersion.tryParse(ver);
1803+
break;
1804+
}
1805+
}
1806+
break;
1807+
}
1808+
// Diagnose unable to checking the current version.
1809+
if (currentVersion.empty()) {
1810+
Impl.diagnose(moduleID.Loc, diag::cannot_find_project_version, "Clang",
1811+
moduleID.Item.str());
1812+
return true;
1813+
}
1814+
assert(!currentVersion.empty());
1815+
// Give a green light if the version on disk is greater or equal to the version
1816+
// specified in the canImport condition.
1817+
return currentVersion >= version;
17521818
}
17531819

17541820
ModuleDecl *ClangImporter::Implementation::loadModuleClang(
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// REQUIRES: VENDOR=apple
2+
// RUN: %empty-directory(%t)
3+
// RUN: %empty-directory(%t/overlaydir)
4+
// RUN: %empty-directory(%t/frameworks)
5+
6+
// RUN: cp -rf %S/Inputs/frameworks/Simple.framework %t/frameworks/
7+
8+
// RUN: echo "current-version: 1830.100" > %t/frameworks/Simple.framework/Simple.tbd
9+
// RUN: echo "@_exported import Simple" > %t.overlay.swift
10+
// RUN: echo "public func additional() {}" >> %t.overlay.swift
11+
12+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -disable-implicit-concurrency-module-import -module-name Simple -F %t/frameworks/ %t.overlay.swift -emit-module-path %t/overlaydir/Simple.swiftmodule
13+
// RUN: %target-typecheck-verify-swift -disable-implicit-concurrency-module-import -I %t/overlaydir/ -F %t/frameworks
14+
15+
import Simple
16+
17+
func canImportVersioned() {
18+
#if canImport(Simple, underlyingVersion: 3.3)
19+
let a = 1 // expected-warning {{initialization of immutable value 'a' was never used; consider replacing with assignment to '_' or removing it}}
20+
#endif
21+
22+
#if canImport(Simple, underlyingVersion: 1830.100)
23+
let b = 1 // expected-warning {{initialization of immutable value 'b' was never used; consider replacing with assignment to '_' or removing it}}
24+
#endif
25+
26+
#if canImport(Simple, underlyingVersion: 1830.11)
27+
let c = 1 // expected-warning {{initialization of immutable value 'c' was never used; consider replacing with assignment to '_' or removing it}}
28+
#endif
29+
}
30+
31+
func canNotImportVersioned() {
32+
#if canImport(Simple, underlyingVersion: 1831)
33+
let a = 1
34+
#endif
35+
36+
#if canImport(Simple, underlyingVersion: 1830.101)
37+
let b = 1
38+
#endif
39+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// REQUIRES: VENDOR=apple
2+
// RUN: %empty-directory(%t)
3+
// RUN: %empty-directory(%t/overlaydir)
4+
// RUN: %empty-directory(%t/frameworks)
5+
6+
// RUN: cp -rf %S/Inputs/frameworks/Simple.framework %t/frameworks/
7+
8+
// RUN: echo "" > %t/frameworks/Simple.framework/Simple.tbd
9+
// RUN: echo "@_exported import Simple" > %t.overlay.swift
10+
// RUN: echo "public func additional() {}" >> %t.overlay.swift
11+
12+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -disable-implicit-concurrency-module-import -module-name Simple -F %t/frameworks/ %t.overlay.swift -emit-module-path %t/overlaydir/Simple.swiftmodule
13+
// RUN: %target-typecheck-verify-swift -disable-implicit-concurrency-module-import -I %t/overlaydir/ -F %t/frameworks
14+
15+
import Simple
16+
17+
func canImportVersioned() {
18+
#if canImport(Simple, underlyingVersion: 3.3) // expected-warning {{cannot find user version number for Clang module 'Simple'; version number ignored}}
19+
#endif
20+
}

0 commit comments

Comments
 (0)