Skip to content

Commit 2ab97ed

Browse files
authored
Merge pull request #40494 from apple/es-complete
[Module Aliasing] Add module aliasing option to swift-ide-test Add module aliasing handling in code complete Resolves rdar://86294338
2 parents b92f0b6 + 4ad3a55 commit 2ab97ed

9 files changed

+367
-55
lines changed

include/swift/Frontend/Frontend.h

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

312+
/// Sets the module alias map with string args passed in via `-module-alias`.
313+
/// \param args The arguments to `-module-alias`. If input has `-module-alias Foo=Bar
314+
/// -module-alias Baz=Qux`, the args are ['Foo=Bar', 'Baz=Qux']. The name
315+
/// Foo is the name that appears in source files, while it maps to Bar, the name
316+
/// of the binary on disk, /path/to/Bar.swiftmodule(interface), under the hood.
317+
/// \param diags Used to print diagnostics in case validation of the string args fails.
318+
/// See \c ModuleAliasesConverter::computeModuleAliases on validation details.
319+
/// \return Whether setting module alias map succeeded; false if args validation fails.
320+
bool setModuleAliasMap(std::vector<std::string> args, DiagnosticEngine &diags);
321+
312322
std::string getOutputFilename() const {
313323
return FrontendOpts.InputsAndOutputs.getSingleOutputFilename();
314324
}

lib/Frontend/ArgsToFrontendOptionsConverter.cpp

Lines changed: 65 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,9 @@ bool ArgsToFrontendOptionsConverter::convert(
253253
Opts.ModuleLinkName = A->getValue();
254254

255255
// This must be called after computing module name, module abi name,
256-
// and module link name.
257-
if (computeModuleAliases())
256+
// and module link name. If computing module aliases is unsuccessful,
257+
// return early.
258+
if (!computeModuleAliases())
258259
return true;
259260

260261
if (const Arg *A = Args.getLastArg(OPT_access_notes_path))
@@ -521,58 +522,7 @@ bool ArgsToFrontendOptionsConverter::setUpImmediateArgs() {
521522

522523
bool ArgsToFrontendOptionsConverter::computeModuleAliases() {
523524
auto list = Args.getAllArgValues(options::OPT_module_alias);
524-
if (!list.empty()) {
525-
auto validate = [this](StringRef value, bool allowModuleName) -> bool
526-
{
527-
if (!allowModuleName) {
528-
if (value == Opts.ModuleName ||
529-
value == Opts.ModuleABIName ||
530-
value == Opts.ModuleLinkName) {
531-
Diags.diagnose(SourceLoc(), diag::error_module_alias_forbidden_name, value);
532-
return false;
533-
}
534-
}
535-
if (value == STDLIB_NAME) {
536-
Diags.diagnose(SourceLoc(), diag::error_module_alias_forbidden_name, value);
537-
return false;
538-
}
539-
if (!Lexer::isIdentifier(value)) {
540-
Diags.diagnose(SourceLoc(), diag::error_bad_module_name, value, false);
541-
return false;
542-
}
543-
return true;
544-
};
545-
546-
for (auto item: list) {
547-
auto str = StringRef(item);
548-
// splits to an alias and the underlying name
549-
auto pair = str.split('=');
550-
auto lhs = pair.first;
551-
auto rhs = pair.second;
552-
553-
if (rhs.empty()) { // '=' is missing
554-
Diags.diagnose(SourceLoc(), diag::error_module_alias_invalid_format, str);
555-
return true;
556-
}
557-
if (!validate(lhs, false) || !validate(rhs, true)) {
558-
return true;
559-
}
560-
561-
// First, add the underlying name as a key to prevent it from being
562-
// used as an alias
563-
if (!Opts.ModuleAliasMap.insert({rhs, StringRef()}).second) {
564-
Diags.diagnose(SourceLoc(), diag::error_module_alias_duplicate, rhs);
565-
return true;
566-
}
567-
// Next, add the alias as a key and the underlying name as a value to the map
568-
auto underlyingName = Opts.ModuleAliasMap.find(rhs)->first();
569-
if (!Opts.ModuleAliasMap.insert({lhs, underlyingName}).second) {
570-
Diags.diagnose(SourceLoc(), diag::error_module_alias_duplicate, lhs);
571-
return true;
572-
}
573-
}
574-
}
575-
return false;
525+
return ModuleAliasesConverter::computeModuleAliases(list, Opts, Diags);
576526
}
577527

578528
bool ArgsToFrontendOptionsConverter::computeModuleName() {
@@ -753,3 +703,64 @@ void ArgsToFrontendOptionsConverter::computeLLVMArgs() {
753703
Opts.LLVMArgs.push_back(A->getValue());
754704
}
755705
}
706+
707+
bool ModuleAliasesConverter::computeModuleAliases(std::vector<std::string> args,
708+
FrontendOptions &options,
709+
DiagnosticEngine &diags) {
710+
if (!args.empty()) {
711+
// ModuleAliasMap should initially be empty as setting
712+
// it should be called only once
713+
options.ModuleAliasMap.clear();
714+
715+
auto validate = [&options, &diags](StringRef value, bool allowModuleName) -> bool
716+
{
717+
if (!allowModuleName) {
718+
if (value == options.ModuleName ||
719+
value == options.ModuleABIName ||
720+
value == options.ModuleLinkName) {
721+
diags.diagnose(SourceLoc(), diag::error_module_alias_forbidden_name, value);
722+
return false;
723+
}
724+
}
725+
if (value == STDLIB_NAME) {
726+
diags.diagnose(SourceLoc(), diag::error_module_alias_forbidden_name, value);
727+
return false;
728+
}
729+
if (!Lexer::isIdentifier(value)) {
730+
diags.diagnose(SourceLoc(), diag::error_bad_module_name, value, false);
731+
return false;
732+
}
733+
return true;
734+
};
735+
736+
for (auto item: args) {
737+
auto str = StringRef(item);
738+
// splits to an alias and the underlying name
739+
auto pair = str.split('=');
740+
auto lhs = pair.first;
741+
auto rhs = pair.second;
742+
743+
if (rhs.empty()) { // '=' is missing
744+
diags.diagnose(SourceLoc(), diag::error_module_alias_invalid_format, str);
745+
return false;
746+
}
747+
if (!validate(lhs, false) || !validate(rhs, true)) {
748+
return false;
749+
}
750+
751+
// First, add the underlying name as a key to prevent it from being
752+
// used as an alias
753+
if (!options.ModuleAliasMap.insert({rhs, StringRef()}).second) {
754+
diags.diagnose(SourceLoc(), diag::error_module_alias_duplicate, rhs);
755+
return false;
756+
}
757+
// Next, add the alias as a key and the underlying name as a value to the map
758+
auto underlyingName = options.ModuleAliasMap.find(rhs)->first();
759+
if (!options.ModuleAliasMap.insert({lhs, underlyingName}).second) {
760+
diags.diagnose(SourceLoc(), diag::error_module_alias_duplicate, lhs);
761+
return false;
762+
}
763+
}
764+
}
765+
return true;
766+
}

lib/Frontend/ArgsToFrontendOptionsConverter.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,22 @@ class ArgsToFrontendOptionsConverter {
7373
determineRequestedAction(const llvm::opt::ArgList &);
7474
};
7575

76+
class ModuleAliasesConverter {
77+
public:
78+
/// Sets the \c ModuleAliasMap in the \c FrontendOptions with args passed via `-module-alias`.
79+
///
80+
/// \param args The arguments to `-module-alias`. If input has `-module-alias Foo=Bar
81+
/// -module-alias Baz=Qux`, the args are ['Foo=Bar', 'Baz=Qux']. The name
82+
/// Foo is the name that appears in source files, while it maps to Bar, the name
83+
/// of the binary on disk, /path/to/Bar.swiftmodule(interface), under the hood.
84+
/// \param options FrontendOptions containings the module alias map to set args to.
85+
/// \param diags Used to print diagnostics in case validation of the args fails.
86+
/// \return Whether the validation passed and successfully set the module alias map
87+
static bool computeModuleAliases(std::vector<std::string> args,
88+
FrontendOptions &options,
89+
DiagnosticEngine &diags);
90+
};
91+
7692
} // namespace swift
7793

7894
#endif /* SWIFT_FRONTEND_ARGSTOFRONTENDOPTIONSCONVERTER_H */

lib/Frontend/CompilerInvocation.cpp

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

266+
bool CompilerInvocation::setModuleAliasMap(std::vector<std::string> args,
267+
DiagnosticEngine &diags) {
268+
return ModuleAliasesConverter::computeModuleAliases(args, FrontendOpts, diags);
269+
}
270+
266271
static bool ParseFrontendArgs(
267272
FrontendOptions &opts, ArgList &args, DiagnosticEngine &diags,
268273
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
@@ -2206,7 +2206,18 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
22062206
SemanticContextKind::None,
22072207
expectedTypeContext);
22082208
Builder.setAssociatedDecl(MD);
2209-
Builder.addBaseName(MD->getNameStr());
2209+
auto moduleName = MD->getName();
2210+
2211+
// This checks if module aliasing was used. For example, when editing
2212+
// `import ...`, and `-module-alias Foo=Bar` was passed, we want to show
2213+
// Foo as an option to import, instead of Bar (name of the binary), as
2214+
// Foo is the name that should appear in source files.
2215+
auto aliasedName = Ctx.getRealModuleName(moduleName, ASTContext::ModuleAliasLookupOption::aliasFromRealName);
2216+
if (aliasedName != moduleName && // check if module aliasing was applied
2217+
!aliasedName.empty()) { // check an alias mapped to the binary name exists
2218+
moduleName = aliasedName; // if so, use the aliased name
2219+
}
2220+
Builder.addBaseName(moduleName.str());
22102221
Builder.addTypeAnnotation("Module");
22112222
if (R)
22122223
Builder.setNotRecommended(*R);
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %{python} %utils/split_file.py -o %t %s
3+
4+
// RUN: %empty-directory(%t/Modules)
5+
// RUN: %target-swift-frontend -emit-module -module-name AppleLogging -module-alias XLogging=AppleLogging -o %t/Modules %t/FileLogging.swift
6+
7+
// BEGIN FileLogging.swift
8+
public struct Logger {
9+
public init() {}
10+
}
11+
12+
public protocol Logging {
13+
var name: String { get }
14+
}
15+
16+
public func setupLogger() -> XLogging.Logger? {
17+
return Logger()
18+
}
19+
20+
// RUN: %empty-directory(%t/Out)
21+
// RUN: %target-swift-ide-test -batch-code-completion -source-filename %t/FileLib1.swift -module-alias XLogging=AppleLogging -filecheck %raw-FileCheck -completion-output-dir %t/Out -I %t/Modules
22+
23+
// BEGIN FileLib1.swift
24+
import XLogging
25+
26+
func testModuleNameInBody() {
27+
#^EXPR^#
28+
}
29+
30+
// EXPR: Begin completion
31+
// EXPR-NOT: AppleLogging
32+
// EXPR-DAG: Decl[Module]/None: XLogging[#Module#]; name=XLogging
33+
// EXPR-DAG: Decl[Protocol]/OtherModule[XLogging]/Flair[RareType]: Logging[#Logging#]; name=Logging
34+
// EXPR-DAG: Decl[Struct]/OtherModule[XLogging]: Logger[#Logger#]; name=Logger
35+
// EXPR-DAG: Decl[FreeFunction]/OtherModule[XLogging]: setupLogger()[#Logger?#]; name=setupLogger()
36+
// EXPR: End completions
37+
38+
// RUN: %empty-directory(%t/Out)
39+
// RUN: %target-swift-ide-test -batch-code-completion -source-filename %t/FileLib2.swift -module-alias XLogging=AppleLogging -filecheck %raw-FileCheck -completion-output-dir %t/Out -I %t/Modules
40+
41+
// BEGIN FileLib2.swift
42+
import XLogging
43+
44+
class ModuleNameInClause: #^MODULE^# {
45+
}
46+
47+
// MODULE: Begin completion
48+
// MODULE-NOT: AppleLogging
49+
// MODULE-DAG: Decl[Module]/None: XLogging[#Module#]; name=XLogging
50+
51+
// MODULE-DAG: Decl[Protocol]/OtherModule[XLogging]: Logging[#Logging#]; name=Logging
52+
// MODULE-DAG: Decl[Struct]/OtherModule[XLogging]: Logger[#Logger#]; name=Logger
53+
// MODULE: End completions
54+
55+
// RUN: %empty-directory(%t/Out)
56+
// RUN: %target-swift-ide-test -batch-code-completion -source-filename %t/FileLib3.swift -module-alias XLogging=AppleLogging -filecheck %raw-FileCheck -completion-output-dir %t/Out -I %t/Modules
57+
58+
// BEGIN FileLib3.swift
59+
import XLogging
60+
61+
func testModuleNameInDecl() -> #^TYPE^# {
62+
}
63+
64+
// TYPE: Begin completion
65+
// TYPE-NOT: AppleLogging
66+
// TYPE-DAG: Decl[Module]/None: XLogging[#Module#]; name=XLogging
67+
// TYPE-DAG: Decl[Protocol]/OtherModule[XLogging]: Logging[#Logging#]; name=Logging
68+
// TYPE-DAG: Decl[Struct]/OtherModule[XLogging]: Logger[#Logger#]; name=Logger
69+
// TYPE: End completions
70+
71+
// RUN: %empty-directory(%t/Out)
72+
// RUN: %target-swift-ide-test -batch-code-completion -source-filename %t/FileLib4.swift -module-alias XLogging=AppleLogging -filecheck %raw-FileCheck -completion-output-dir %t/Out -I %t/Modules
73+
74+
// BEGIN FileLib4.swift
75+
import #^IMPORT^#
76+
77+
// IMPORT: Begin completion
78+
// IMPORT-NOT: AppleLogging
79+
// IMPORT: Decl[Module]/None: XLogging[#Module#]; name=XLogging
80+
// IMPORT: End completions

0 commit comments

Comments
 (0)