Skip to content

Commit 30b2f05

Browse files
authored
Handle 'import struct Foo' where Foo is a non-nominal typealias (#14797)
Previously this just crashed. Now it suggests `import typealias Foo`, a syntax that works back to Swift 1. rdar://problem/36756349
1 parent 4edb8d9 commit 30b2f05

File tree

5 files changed

+69
-11
lines changed

5 files changed

+69
-11
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -688,9 +688,13 @@ ERROR(decl_does_not_exist_in_module,none,
688688
"%1 does not exist in module %2",
689689
(/*ImportKind*/ unsigned, Identifier, Identifier))
690690
ERROR(imported_decl_is_wrong_kind,none,
691-
"%0 was imported as '%1', but is a "
692-
"%select{%error|type|struct|class|enum|protocol|variable|function}2",
691+
"%0 was imported as '%1', but is "
692+
"%select{%error|a type|a struct|a class|an enum|a protocol|a variable|"
693+
"a function}2",
693694
(Identifier, StringRef, /*ImportKind*/ unsigned))
695+
ERROR(imported_decl_is_wrong_kind_typealias,none,
696+
"%0 %1 cannot be imported as '%2'",
697+
(DescriptiveDeclKind, Type, StringRef))
694698
ERROR(ambiguous_decl_in_module,none,
695699
"ambiguous name %0 in module %1", (Identifier, Identifier))
696700

lib/AST/Decl.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -804,7 +804,10 @@ ImportKind ImportDecl::getBestImportKind(const ValueDecl *VD) {
804804

805805
case DeclKind::TypeAlias: {
806806
Type type = cast<TypeAliasDecl>(VD)->getDeclaredInterfaceType();
807-
return getBestImportKind(type->getAnyNominal());
807+
auto *nominal = type->getAnyNominal();
808+
if (!nominal)
809+
return ImportKind::Type;
810+
return getBestImportKind(nominal);
808811
}
809812

810813
case DeclKind::Accessor:

lib/Sema/NameBinding.cpp

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,22 @@ static bool isCompatibleImportKind(ImportKind expected, ImportKind actual) {
116116
llvm_unreachable("Unhandled ImportKind in switch.");
117117
}
118118

119+
static bool isNominalImportKind(ImportKind kind) {
120+
switch (kind) {
121+
case ImportKind::Module:
122+
llvm_unreachable("module imports do not bring in decls");
123+
case ImportKind::Struct:
124+
case ImportKind::Class:
125+
case ImportKind::Enum:
126+
case ImportKind::Protocol:
127+
return true;
128+
case ImportKind::Type:
129+
case ImportKind::Var:
130+
case ImportKind::Func:
131+
return false;
132+
}
133+
}
134+
119135
static const char *getImportKindString(ImportKind kind) {
120136
switch (kind) {
121137
case ImportKind::Module:
@@ -258,12 +274,30 @@ void NameBinder::addImport(
258274
diagnose(next, diag::found_candidate);
259275

260276
} else if (!isCompatibleImportKind(ID->getImportKind(), *actualKind)) {
261-
diagnose(ID, diag::imported_decl_is_wrong_kind,
262-
declPath.front().first,
263-
getImportKindString(ID->getImportKind()),
264-
static_cast<unsigned>(*actualKind))
265-
.fixItReplace(SourceRange(ID->getKindLoc()),
266-
getImportKindString(*actualKind));
277+
Optional<InFlightDiagnostic> emittedDiag;
278+
if (*actualKind == ImportKind::Type &&
279+
isNominalImportKind(ID->getImportKind())) {
280+
assert(decls.size() == 1 &&
281+
"if we start suggesting ImportKind::Type for, e.g., a mix of "
282+
"structs and classes, we'll need a different message here");
283+
assert(isa<TypeAliasDecl>(decls.front()) &&
284+
"ImportKind::Type is only the best choice for a typealias");
285+
auto *typealias = cast<TypeAliasDecl>(decls.front());
286+
emittedDiag.emplace(diagnose(ID,
287+
diag::imported_decl_is_wrong_kind_typealias,
288+
typealias->getDescriptiveKind(),
289+
typealias->getDeclaredInterfaceType(),
290+
getImportKindString(ID->getImportKind())));
291+
} else {
292+
emittedDiag.emplace(diagnose(ID, diag::imported_decl_is_wrong_kind,
293+
declPath.front().first,
294+
getImportKindString(ID->getImportKind()),
295+
static_cast<unsigned>(*actualKind)));
296+
}
297+
298+
emittedDiag->fixItReplace(SourceRange(ID->getKindLoc()),
299+
getImportKindString(*actualKind));
300+
emittedDiag->flush();
267301

268302
if (decls.size() == 1)
269303
diagnose(decls.front(), diag::decl_declared_here,
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
public var x: Int = 0
2+
3+
public enum Choice {
4+
case yes, no, maybeSo
5+
}
6+
7+
public typealias Callback = () -> Void
8+
9+
public typealias Pair<T> = (T, T)

test/NameBinding/import-specific-fixits.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
// RUN: %target-swift-frontend -emit-module -o %t %S/Inputs/ambiguous_left.swift
44
// RUN: %target-swift-frontend -emit-module -o %t %S/Inputs/ambiguous_right.swift
55
// RUN: %target-swift-frontend -emit-module -o %t -I %t %S/Inputs/ambiguous.swift
6+
// RUN: %target-swift-frontend -emit-module -o %t %S/Inputs/DeclsUsedWrongly.swift
67

7-
// RUN: echo "public var x = Int()" | %target-swift-frontend -module-name FooBar -emit-module -o %t -
88
// RUN: %target-swift-frontend -typecheck -I %t -serialize-diagnostics-path %t.dia %s -verify
99
// RUN: c-index-test -read-diagnostics %t.dia > %t.deserialized_diagnostics.txt 2>&1
1010
// RUN: %FileCheck --input-file=%t.deserialized_diagnostics.txt %s
@@ -29,7 +29,15 @@ import class Swift.Int64 // expected-error {{'Int64' was imported as 'class', bu
2929

3030
import class Swift.Bool // expected-error {{'Bool' was imported as 'class', but is a struct}} {{8-13=struct}}
3131

32-
import struct FooBar.x // expected-error {{'x' was imported as 'struct', but is a variable}} {{8-14=var}}
32+
import struct DeclsUsedWrongly.x // expected-error {{'x' was imported as 'struct', but is a variable}} {{8-14=var}}
33+
34+
import struct DeclsUsedWrongly.Choice // expected-error {{'Choice' was imported as 'struct', but is an enum}} {{8-14=enum}}
35+
36+
import struct DeclsUsedWrongly.Callback // expected-error {{type alias 'Callback' (aka '() -> ()') cannot be imported as 'struct'}} {{8-14=typealias}}
37+
import var DeclsUsedWrongly.Callback // expected-error {{'Callback' was imported as 'var', but is a type}} {{8-11=typealias}}
38+
39+
import struct DeclsUsedWrongly.Pair // expected-error {{type alias 'Pair' cannot be imported as 'struct'}} {{8-14=typealias}}
40+
import var DeclsUsedWrongly.Pair // expected-error {{'Pair' was imported as 'var', but is a type}} {{8-11=typealias}}
3341

3442
import struct Swift.print // expected-error {{'print' was imported as 'struct', but is a function}} {{8-14=func}}
3543

0 commit comments

Comments
 (0)