Skip to content

Commit 93132b8

Browse files
committed
Add module aliasing option to swift-ide-test
Add module aliasing handling in code complete Resolves rdar://86294338
1 parent e358302 commit 93132b8

File tree

6 files changed

+181
-1
lines changed

6 files changed

+181
-1
lines changed

include/swift/Frontend/Frontend.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,8 @@ class CompilerInvocation {
309309
return FrontendOpts.ModuleName;
310310
}
311311

312+
void setModuleAliasMap(std::vector<std::string> args);
313+
312314
std::string getOutputFilename() const {
313315
return FrontendOpts.InputsAndOutputs.getSingleOutputFilename();
314316
}

lib/Frontend/ArgsToFrontendOptionsConverter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,10 @@ bool ArgsToFrontendOptionsConverter::setUpImmediateArgs() {
516516
bool ArgsToFrontendOptionsConverter::computeModuleAliases() {
517517
auto list = Args.getAllArgValues(options::OPT_module_alias);
518518
if (!list.empty()) {
519+
// ModuleAliasMap should initially be empty as setting
520+
// it should be called only once
521+
Opts.ModuleAliasMap.clear();
522+
519523
auto validate = [this](StringRef value, bool allowModuleName) -> bool
520524
{
521525
if (!allowModuleName) {

lib/Frontend/CompilerInvocation.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,33 @@ void CompilerInvocation::setSDKPath(const std::string &Path) {
262262
updateRuntimeLibraryPaths(SearchPathOpts, LangOpts.Target);
263263
}
264264

265+
// This assumes the param args contains valid strings for module
266+
// aliasing. The full validation of input for aliasing is done at
267+
// ArgsToFrontendOptionsConverter::computeModulealiases.
268+
void CompilerInvocation::setModuleAliasMap(std::vector<std::string> args) {
269+
// ModuleAliasMap should initially be empty as setting
270+
// it should be called only once
271+
FrontendOpts.ModuleAliasMap.clear();
272+
for (auto item: args) {
273+
auto str = StringRef(item);
274+
// splits to an alias and the underlying name
275+
auto pair = str.split('=');
276+
auto lhs = pair.first;
277+
auto rhs = pair.second;
278+
if (rhs.empty()) // bad format, so skip to the next
279+
continue;
280+
if (!FrontendOpts.ModuleAliasMap.insert({rhs, StringRef()}).second) {
281+
// the underlying name was already added, so skip
282+
continue;
283+
}
284+
auto underlyingName = FrontendOpts.ModuleAliasMap.find(rhs)->first();
285+
if (!FrontendOpts.ModuleAliasMap.insert({lhs, underlyingName}).second) {
286+
// the alias was already added, so skip
287+
continue;
288+
}
289+
}
290+
}
291+
265292
static bool ParseFrontendArgs(
266293
FrontendOptions &opts, ArgList &args, DiagnosticEngine &diags,
267294
SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>> *buffers) {

lib/IDE/CodeCompletion.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2204,7 +2204,18 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
22042204
SemanticContextKind::None,
22052205
expectedTypeContext);
22062206
Builder.setAssociatedDecl(MD);
2207-
Builder.addBaseName(MD->getNameStr());
2207+
auto moduleName = MD->getName();
2208+
2209+
// This checks if module aliasing was used. For example, when editing
2210+
// `import ...`, and `-module-alias Foo=Bar` was passed, we want to show
2211+
// Foo as an option to import, instead of Bar (name of the binary), as
2212+
// Foo is the name that should appear in source files.
2213+
auto aliasedName = Ctx.getRealModuleName(moduleName, ASTContext::ModuleAliasLookupOption::aliasFromRealName);
2214+
if (aliasedName != moduleName && // check if module aliasing was applied
2215+
!aliasedName.empty()) { // check an alias mapped to the binary name exists
2216+
moduleName = aliasedName; // if so, use the aliased name
2217+
}
2218+
Builder.addBaseName(moduleName.str());
22082219
Builder.addTypeAnnotation("Module");
22092220
if (R)
22102221
Builder.setNotRecommended(*R);
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/// Test code completion with module aliasing
2+
/// When -module-alias <alias_name>=<real_name> is applied, code completion should show
3+
/// the <alias_name> as that's the name which should appear in source files including import statements,
4+
/// decls, expressions, etc. while getting visible decls come from the module of <real_name>.
5+
/// Below, XLogging is the alias and mapped to the real name AppleLogging. Note that the real name
6+
/// AppleLogging should not appear in the code completion results.
7+
///
8+
// RUN: %empty-directory(%t)
9+
// RUN: %{python} %utils/split_file.py -o %t %s
10+
11+
// RUN: %target-swift-frontend %t/FileLogging.swift -module-name AppleLogging -module-alias XLogging=AppleLogging -emit-module -o %t/AppleLogging.swiftmodule
12+
13+
// RUN: %target-swift-ide-test -code-completion -code-completion-token=MODULE_NAME -source-filename %t/FileLib1.swift -module-alias XLogging=AppleLogging -I %t > %t/result1.txt
14+
// RUN: %FileCheck %s -check-prefix CHECK1 < %t/result1.txt
15+
16+
// CHECK1-NOT: AppleLogging
17+
// CHECK1: found code completion token MODULE_NAME at offset 43
18+
// CHECK1: Begin completions, 335 items
19+
// CHECK1: Decl[Module]/None: XLogging[#Module#]; name=XLogging
20+
// CHECK1: Decl[Protocol]/OtherModule[XLogging]: Logging[#Logging#]; name=Logging
21+
// CHECK1: Decl[Struct]/OtherModule[XLogging]: Logger[#Logger#]; name=Logger
22+
// CHECK1: End completions
23+
24+
25+
// RUN: %target-swift-ide-test -code-completion -code-completion-token=MODULE_NAME -source-filename %t/FileLib2.swift -module-alias XLogging=AppleLogging -I %t > %t/result2.txt
26+
// RUN: %FileCheck %s -check-prefix CHECK2 < %t/result2.txt
27+
28+
// CHECK2-NOT: AppleLogging
29+
// CHECK2: found code completion token MODULE_NAME at offset 48
30+
// CHECK2: Begin completions, 335 items
31+
// CHECK2: Decl[Module]/None: XLogging[#Module#]; name=XLogging
32+
// CHECK2: Decl[Protocol]/OtherModule[XLogging]: Logging[#Logging#]; name=Logging
33+
// CHECK2: Decl[Struct]/OtherModule[XLogging]: Logger[#Logger#]; name=Logger
34+
// CHECK2: End completions
35+
36+
37+
// RUN: %target-swift-ide-test -code-completion -code-completion-token=MODULE_NAME -source-filename %t/FileLib3.swift -module-alias XLogging=AppleLogging -I %t > %t/result3.txt
38+
// RUN: %FileCheck %s -check-prefix CHECK3 < %t/result3.txt
39+
40+
// CHECK3-NOT: AppleLogging
41+
// CHECK3: found code completion token MODULE_NAME at offset 49
42+
// CHECK3: Begin completions, 516 items
43+
// CHECK3: Decl[Module]/None: XLogging[#Module#]; name=XLogging
44+
// CHECK3: Decl[Protocol]/OtherModule[XLogging]/Flair[RareType]: Logging[#Logging#]; name=Logging
45+
// CHECK3: Decl[Struct]/OtherModule[XLogging]: Logger[#Logger#]; name=Logger
46+
// CHECK3: Decl[FreeFunction]/OtherModule[XLogging]: setupLogger()[#Logger?#]; name=setupLogger()
47+
// CHECK3: End completions
48+
49+
50+
// RUN: %target-swift-ide-test -code-completion -code-completion-token=MODULE_NAME -source-filename %t/FileLib4.swift -module-alias XLogging=AppleLogging -I %t > %t/result4.txt
51+
// RUN: %FileCheck %s -check-prefix CHECK4 < %t/result4.txt
52+
53+
// CHECK4-NOT: AppleLogging
54+
// CHECK4: found code completion token MODULE_NAME at offset 58
55+
// CHECK4: Begin completions, 3 items
56+
// CHECK4: Decl[Protocol]/OtherModule[XLogging]/Flair[RareType]: Logging[#Logging#]; name=Logging
57+
// CHECK4: Decl[Struct]/OtherModule[XLogging]: Logger[#Logger#]; name=Logger
58+
// CHECK4: Decl[FreeFunction]/OtherModule[XLogging]: setupLogger()[#Logger?#]; name=setupLogger()
59+
// CHECK4: End completions
60+
61+
/// In the following, the module alias name should be shown as a module that can be imported instead of the real name
62+
///
63+
// RUN: %target-swift-ide-test -code-completion -code-completion-token=MODULE_NAME -source-filename %t/FileLib5.swift -module-alias XLogging=AppleLogging -I %t > %t/result5.txt
64+
// RUN: %FileCheck %s -check-prefix CHECK5 < %t/result5.txt
65+
66+
/// In search paths, only AppleLogging.swiftmodule exists, but when `-module-alias XLogging=AppleLogging` is passed,
67+
/// we want to only show XLogging as an option to import, not AppleLogging
68+
// CHECK5-NOT: AppleLogging
69+
// CHECK5: found code completion token MODULE_NAME at offset 7
70+
// CHECK5: Begin completions, 233 items
71+
// CHECK5: Decl[Module]/None: XLogging[#Module#]; name=XLogging
72+
// CHECK5: End completions
73+
74+
75+
// RUN: %target-swift-ide-test -code-completion -code-completion-token=MODULE_NAME -source-filename %t/FileLib5.swift -I %t > %t/result6.txt
76+
// RUN: %FileCheck %s -check-prefix CHECK6 < %t/result6.txt
77+
78+
/// In search paths, only AppleLogging.swiftmodule exists, and no module aliasing option is passed, so
79+
/// just show AppleLogging as one of the modules that can be imported
80+
// CHECK6-NOT: XLogging
81+
// CHECK6: found code completion token MODULE_NAME at offset 7
82+
// CHECK6: Begin completions, 233 items
83+
// CHECK6: Decl[Module]/None: AppleLogging[#Module#]; name=AppleLogging
84+
// CHECK6: End completions
85+
86+
87+
// BEGIN FileLogging.swift
88+
public struct Logger {
89+
public init() {}
90+
}
91+
92+
public protocol Logging {
93+
var content: String { get }
94+
}
95+
96+
public func setupLogger() -> XLogging.Logger? {
97+
return Logger()
98+
}
99+
100+
// BEGIN FileLib1.swift
101+
import XLogging
102+
103+
class ModuleNameInClause: #^MODULE_NAME^# {
104+
}
105+
106+
// BEGIN FileLib2.swift
107+
import XLogging
108+
109+
func testModuleNameInDecl() -> #^MODULE_NAME^# {
110+
}
111+
112+
// BEGIN FileLib3.swift
113+
import XLogging
114+
115+
func testModuleNameInBody() {
116+
#^MODULE_NAME^#
117+
}
118+
119+
// BEGIN FileLib4.swift
120+
import XLogging
121+
122+
func testModuleNameInBody() {
123+
XLogging.#^MODULE_NAME^#
124+
}
125+
126+
// BEGIN FileLib5.swift
127+
import #^MODULE_NAME^#
128+

tools/swift-ide-test/swift-ide-test.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,10 @@ EnableCrossImportOverlays("enable-cross-import-overlays",
338338
llvm::cl::desc("Automatically import declared cross-import overlays."),
339339
llvm::cl::cat(Category),
340340
llvm::cl::init(false));
341+
static llvm::cl::list<std::string>
342+
ModuleAliases("module-alias",
343+
llvm::cl::desc("Use '-module-alias <name>=<underlying_name>' to map a module of <name> to a different name"),
344+
llvm::cl::cat(Category));
341345

342346
static llvm::cl::opt<bool>
343347
SkipDeinit("skip-deinit",
@@ -4233,6 +4237,10 @@ int main(int argc, char *argv[]) {
42334237
InitInvok.getLangOptions().AllowModuleWithCompilerErrors = true;
42344238
}
42354239

4240+
if (!options::ModuleAliases.empty()) {
4241+
InitInvok.setModuleAliasMap(options::ModuleAliases);
4242+
}
4243+
42364244
// Process the clang arguments last and allow them to override previously
42374245
// set options.
42384246
if (!CCArgs.empty()) {

0 commit comments

Comments
 (0)