Skip to content

[DiagnosticsQoI] Fully Qualify the Parent Type When Diagnosing Missing Member Types #34648

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions include/swift/AST/DiagnosticEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,24 @@ namespace swift {
typedef T type;
};
}


/// A family of wrapper types for compiler data types that forces its
/// underlying data to be formatted with full qualification.
///
/// So far, this is only useful for \c Type, hence the SFINAE'ing.
template <typename T, typename = void> struct FullyQualified {};

template <typename T>
struct FullyQualified<
T, typename std::enable_if<std::is_convertible<T, Type>::value>::type> {
Type t;

public:
FullyQualified(T t) : t(t){};

Type getType() const { return t; }
};

/// Describes the kind of diagnostic argument we're storing.
///
enum class DiagnosticArgumentKind {
Expand All @@ -86,6 +103,7 @@ namespace swift {
ValueDecl,
Type,
TypeRepr,
FullyQualifiedType,
PatternKind,
SelfAccessKind,
ReferenceOwnership,
Expand Down Expand Up @@ -116,6 +134,7 @@ namespace swift {
ValueDecl *TheValueDecl;
Type TypeVal;
TypeRepr *TyR;
FullyQualified<Type> FullyQualifiedTypeVal;
PatternKind PatternKindVal;
SelfAccessKind SelfAccessKindVal;
ReferenceOwnership ReferenceOwnershipVal;
Expand Down Expand Up @@ -172,6 +191,10 @@ namespace swift {
: Kind(DiagnosticArgumentKind::TypeRepr), TyR(T) {
}

DiagnosticArgument(FullyQualified<Type> FQT)
: Kind(DiagnosticArgumentKind::FullyQualifiedType),
FullyQualifiedTypeVal(FQT) {}

DiagnosticArgument(const TypeLoc &TL) {
if (TypeRepr *tyR = TL.getTypeRepr()) {
Kind = DiagnosticArgumentKind::TypeRepr;
Expand Down Expand Up @@ -268,7 +291,12 @@ namespace swift {
assert(Kind == DiagnosticArgumentKind::TypeRepr);
return TyR;
}


FullyQualified<Type> getAsFullyQualifiedType() const {
assert(Kind == DiagnosticArgumentKind::FullyQualifiedType);
return FullyQualifiedTypeVal;
}

PatternKind getAsPatternKind() const {
assert(Kind == DiagnosticArgumentKind::PatternKind);
return PatternKindVal;
Expand Down
3 changes: 2 additions & 1 deletion include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,8 @@ NOTE(invalid_redecl_implicit_wrapper,none,
ERROR(ambiguous_type_base,none,
"%0 is ambiguous for type lookup in this context", (DeclNameRef))
ERROR(invalid_member_type,none,
"%0 is not a member type of %1", (DeclNameRef, Type))
"%0 is not a member type of %1 %2",
(DeclNameRef, DescriptiveDeclKind, FullyQualified<Type>))
ERROR(invalid_member_type_suggest,none,
"%0 does not have a member type named %1; did you mean %2?",
(Type, DeclNameRef, DeclName))
Expand Down
19 changes: 14 additions & 5 deletions lib/AST/DiagnosticEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -526,21 +526,30 @@ static void formatDiagnosticArgument(StringRef Modifier,
Out << FormatOpts.ClosingQuotationMark;
break;

case DiagnosticArgumentKind::FullyQualifiedType:
case DiagnosticArgumentKind::Type: {
assert(Modifier.empty() && "Improper modifier for Type argument");

// Strip extraneous parentheses; they add no value.
auto type = Arg.getAsType()->getWithoutParens();
Type type;
bool needsQualification = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While you are here, what do you think about enabling this by default for generic parameters? It's very common to name generic parameters like T or U which results in diagnostics that say something like "T is not convertible to T" and it would be much better to always qualify it to avoid confusion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think something like a DescriptiveTypeKind would help out there too.

T is not convertible to generic parameter T

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is in most cases we don't know what type we'd get in conversion diagnostics so it'd be better to always qualify generic parameters...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e.g.

protocol P {}
func foo<T: P>(_: T) {}
func bar<T>(t: T) {
  foo(t)
}

This would produce a note which says where 'T' = 'T'

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would have been better if it did say where 'T' = generic parameter bar.T or something similar.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another example with shadowing:

protocol MyProtocol {}

struct Generic<M: MyProtocol> {
    var values: [M] = []

    func valueAt<M>(_ index: Int) -> M? {
        if values.indices.contains(index) {
            return values[index]
        } else {
            return nil
        }
    }
}

Currently results in cannot convert return expression of type 'M' to return type 'M?'

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I'll build the abstractions for this in a follow-up.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds great, thank you!


if (Arg.getKind() == DiagnosticArgumentKind::Type) {
type = Arg.getAsType()->getWithoutParens();
needsQualification = typeSpellingIsAmbiguous(type, Args);
} else {
assert(Arg.getKind() == DiagnosticArgumentKind::FullyQualifiedType);
type = Arg.getAsFullyQualifiedType().getType()->getWithoutParens();
needsQualification = true;
}

// If a type has an unresolved type, print it with syntax sugar removed for
// clarity. For example, print `Array<_>` instead of `[_]`.
if (type->hasUnresolvedType()) {
type = type->getWithoutSyntaxSugar();
}

bool isAmbiguous = typeSpellingIsAmbiguous(type, Args);

if (isAmbiguous && isa<OpaqueTypeArchetypeType>(type.getPointer())) {
if (needsQualification && isa<OpaqueTypeArchetypeType>(type.getPointer())) {
auto opaqueTypeDecl = type->castTo<OpaqueTypeArchetypeType>()->getDecl();

llvm::SmallString<256> NamingDeclText;
Expand All @@ -562,7 +571,7 @@ static void formatDiagnosticArgument(StringRef Modifier,

} else {
auto printOptions = PrintOptions();
printOptions.FullyQualifiedTypes = isAmbiguous;
printOptions.FullyQualifiedTypes = needsQualification;
std::string typeName = type->getString(printOptions);

if (shouldShowAKA(type, typeName)) {
Expand Down
30 changes: 23 additions & 7 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,17 @@ Type TypeResolution::mapTypeIntoContext(Type type) const {
llvm_unreachable("unhandled stage");
}

// FIXME: It would be nice to have a 'DescriptiveTypeKind' abstraction instead.
static DescriptiveDeclKind describeDeclOfType(Type t) {
if (!t) {
return DescriptiveDeclKind::Type;
}
if (auto *NTD = t->getAnyNominal()) {
return NTD->getDescriptiveKind();
}
return DescriptiveDeclKind::Type;
}

Type TypeResolution::resolveDependentMemberType(
Type baseTy, DeclContext *DC,
SourceRange baseRange,
Expand Down Expand Up @@ -211,8 +222,9 @@ Type TypeResolution::resolveDependentMemberType(
if (!singleType) {
auto name = ref->getNameRef();
auto nameLoc = ref->getNameLoc();
ctx.Diags.diagnose(nameLoc, diag::invalid_member_type, name, baseTy)
.highlight(baseRange);
const auto kind = describeDeclOfType(baseTy);
ctx.Diags.diagnose(nameLoc, diag::invalid_member_type, name, kind, baseTy)
.highlight(baseRange);
corrections.noteAllCandidates();

return ErrorType::get(ctx);
Expand Down Expand Up @@ -1160,8 +1172,10 @@ static Type diagnoseUnknownType(TypeResolution resolution,

// Qualified lookup case.
if (!parentType->mayHaveMembers()) {
diags.diagnose(comp->getNameLoc(), diag::invalid_member_type,
comp->getNameRef(), parentType)
const auto kind = describeDeclOfType(parentType);
diags
.diagnose(comp->getNameLoc(), diag::invalid_member_type,
comp->getNameRef(), kind, parentType)
.highlight(parentRange);
return ErrorType::get(ctx);
}
Expand Down Expand Up @@ -1214,9 +1228,11 @@ static Type diagnoseUnknownType(TypeResolution resolution,
parentType)
.highlight(parentRange);
} else {
diags.diagnose(comp->getNameLoc(), diag::invalid_member_type,
comp->getNameRef(), parentType)
.highlight(parentRange);
const auto kind = describeDeclOfType(parentType);
diags
.diagnose(comp->getNameLoc(), diag::invalid_member_type,
comp->getNameRef(), kind, parentType)
.highlight(parentRange);
// Note where the type was defined, this can help diagnose if the user
// expected name lookup to find a module when there's a conflicting type.
if (auto typeDecl = parentType->getNominalOrBoundGenericNominal()) {
Expand Down
2 changes: 1 addition & 1 deletion test/AutoDiff/Sema/differentiable_attr_type_checking.swift
Original file line number Diff line number Diff line change
Expand Up @@ -632,7 +632,7 @@ class Super: Differentiable {
// TODO(TF-632): Fix "'TangentVector' is not a member type of 'Self'" diagnostic.
// The underlying error should appear instead:
// "covariant 'Self' can only appear at the top level of method result type".
// expected-error @+1 2 {{'TangentVector' is not a member type of 'Self'}}
// expected-error @+1 2 {{'TangentVector' is not a member type of type 'Self'}}
func vjpDynamicSelfResult() -> (Self, (Self.TangentVector) -> Self.TangentVector) {
return (self, { $0 })
}
Expand Down
2 changes: 1 addition & 1 deletion test/Constraints/invalid_archetype_constraint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ struct A2<T> : P {
typealias Element = T
}

func toA<S: Empty, AT:P>(_ s: S) -> AT where AT.Element == S.Generator.Element { // expected-error{{'Generator' is not a member type of 'S'}}
func toA<S: Empty, AT:P>(_ s: S) -> AT where AT.Element == S.Generator.Element { // expected-error{{'Generator' is not a member type of type 'S'}}
return AT()
}
4 changes: 2 additions & 2 deletions test/Constraints/invalid_constraint_lookup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ protocol _Collection {
protocol Collection : _Collection, Sequence {
subscript(i: Index) -> Iterator.Element {get set }
}
func insertionSort<C: Mutable> (_ elements: inout C, i: C.Index) { // expected-error {{cannot find type 'Mutable' in scope}} expected-error {{'Index' is not a member type of 'C'}}
var x: C.Iterator.Element = elements[i] // expected-error {{'Iterator' is not a member type of 'C'}}
func insertionSort<C: Mutable> (_ elements: inout C, i: C.Index) { // expected-error {{cannot find type 'Mutable' in scope}} expected-error {{'Index' is not a member type of type 'C'}}
var x: C.Iterator.Element = elements[i] // expected-error {{'Iterator' is not a member type of type 'C'}}
}
2 changes: 1 addition & 1 deletion test/Constraints/same_types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ func test8b<T: Barrable, U: Barrable>(_ t: T, u: U)
}

// rdar://problem/19137463
func rdar19137463<T>(_ t: T) where T.a == T {} // expected-error{{'a' is not a member type of 'T'}}
func rdar19137463<T>(_ t: T) where T.a == T {} // expected-error{{'a' is not a member type of type 'T'}}
rdar19137463(1)


Expand Down
2 changes: 1 addition & 1 deletion test/Generics/associated_type_typo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,4 @@ class D {
typealias BElement = Int // expected-note{{did you mean 'BElement'?}}
}

func typoSuperclass2<T : D>(_: T, _: T.Element) { } // expected-error{{'Element' is not a member type of 'T'}}
func typoSuperclass2<T : D>(_: T, _: T.Element) { } // expected-error{{'Element' is not a member type of type 'T'}}
2 changes: 1 addition & 1 deletion test/Generics/function_defs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ func sameTypeEq<T>(_: T) where T = T {} // expected-error{{use '==' for same-typ

func badTypeConformance1<T>(_: T) where Int : EqualComparable {} // expected-error{{type 'Int' in conformance requirement does not refer to a generic parameter or associated type}}

func badTypeConformance2<T>(_: T) where T.Blarg : EqualComparable { } // expected-error{{'Blarg' is not a member type of 'T'}}
func badTypeConformance2<T>(_: T) where T.Blarg : EqualComparable { } // expected-error{{'Blarg' is not a member type of type 'T'}}

func badTypeConformance3<T>(_: T) where (T) -> () : EqualComparable { }
// expected-error@-1{{type '(T) -> ()' in conformance requirement does not refer to a generic parameter or associated type}}
Expand Down
8 changes: 4 additions & 4 deletions test/Generics/generic_types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ struct XParam<T> { // expected-note{{'XParam' declared here}}
}
}

var xp : XParam<Int>.T = Int() // expected-error{{'T' is not a member type of 'XParam<Int>'}}
var xp : XParam<Int>.T = Int() // expected-error{{'T' is not a member type of generic struct 'generic_types.XParam<Swift.Int>'}}

// Diagnose failure to meet a superclass requirement.
class X1 { }
Expand Down Expand Up @@ -237,11 +237,11 @@ class Bottom<T : Bottom<Top>> {}
// Invalid inheritance clause

struct UnsolvableInheritance1<T : T.A> {}
// expected-error@-1 {{'A' is not a member type of 'T'}}
// expected-error@-1 {{'A' is not a member type of type 'T'}}

struct UnsolvableInheritance2<T : U.A, U : T.A> {}
// expected-error@-1 {{'A' is not a member type of 'U'}}
// expected-error@-2 {{'A' is not a member type of 'T'}}
// expected-error@-1 {{'A' is not a member type of type 'U'}}
// expected-error@-2 {{'A' is not a member type of type 'T'}}

enum X7<T> where X7.X : G { case X } // expected-error{{enum case 'X' is not a member type of 'X7<T>'}}
// expected-error@-1{{cannot find type 'G' in scope}}
6 changes: 3 additions & 3 deletions test/Generics/invalid.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func badDiagnostic3() {
// Crash with missing nested type inside concrete type
class OuterGeneric<T> {
class InnerGeneric<U> where U:OuterGeneric<T.NoSuchType> {
// expected-error@-1 {{'NoSuchType' is not a member type of 'T'}}
// expected-error@-1 {{'NoSuchType' is not a member type of type 'T'}}
func method() {
_ = method
}
Expand Down Expand Up @@ -107,10 +107,10 @@ class P<N> {

// SR-5579
protocol Foo {
associatedtype Bar where Bar.Nonsense == Int // expected-error{{'Nonsense' is not a member type of 'Self.Bar'}}
associatedtype Bar where Bar.Nonsense == Int // expected-error{{'Nonsense' is not a member type of type 'Self.Bar'}}
}

protocol Wibble : Foo where Bar.EvenMoreNonsense == Int { } // expected-error{{'EvenMoreNonsense' is not a member type of 'Self.Bar'}}
protocol Wibble : Foo where Bar.EvenMoreNonsense == Int { } // expected-error{{'EvenMoreNonsense' is not a member type of type 'Self.Bar'}}

// rdar://45271500 - failure to emit a diagnostic
enum Cat<A> {}
Expand Down
12 changes: 6 additions & 6 deletions test/Interop/Cxx/class/access-specifiers-typechecker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ v.PrivateMemberVar = 1 // expected-error {{value of type 'PublicPrivate' has no
PublicPrivate.PrivateStaticMemberVar = 1 // expected-error {{'PublicPrivate' has no member 'PrivateStaticMemberVar'}}
v.privateMemberFunc() // expected-error {{value of type 'PublicPrivate' has no member 'privateMemberFunc'}}

var privateTypedefVar: PublicPrivate.PrivateTypedef // expected-error {{'PrivateTypedef' is not a member type of 'PublicPrivate'}}
var privateStructVar: PublicPrivate.PrivateStruct // expected-error {{'PrivateStruct' is not a member type of 'PublicPrivate'}}
var privateEnumVar: PublicPrivate.PrivateEnum // expected-error {{'PrivateEnum' is not a member type of 'PublicPrivate'}}
var privateTypedefVar: PublicPrivate.PrivateTypedef // expected-error {{'PrivateTypedef' is not a member type of struct 'AccessSpecifiers.PublicPrivate'}}
var privateStructVar: PublicPrivate.PrivateStruct // expected-error {{'PrivateStruct' is not a member type of struct 'AccessSpecifiers.PublicPrivate'}}
var privateEnumVar: PublicPrivate.PrivateEnum // expected-error {{'PrivateEnum' is not a member type of struct 'AccessSpecifiers.PublicPrivate'}}
// TODO: PrivateEnumValue1 and PrivateAnonymousEnumValue1 give the expected
// error, but only because these types of enums (private or public) aren't
// currently imported at all. Once that is fixed, remove this TODO.
print(PublicPrivate.PrivateEnumValue1) // expected-error {{'PublicPrivate' has no member 'PrivateEnumValue1'}}
print(PublicPrivate.PrivateAnonymousEnumValue1) // expected-error {{'PublicPrivate' has no member 'PrivateAnonymousEnumValue1'}}
var privateClosedEnumVar: PublicPrivate.PrivateClosedEnum // expected-error {{'PrivateClosedEnum' is not a member type of 'PublicPrivate'}}
var privateOpenEnumVar: PublicPrivate.PrivateOpenEnum // expected-error {{'PrivateOpenEnum' is not a member type of 'PublicPrivate'}}
var privateFlagEnumVar: PublicPrivate.PrivateFlagEnum // expected-error {{'PrivateFlagEnum' is not a member type of 'PublicPrivate'}}
var privateClosedEnumVar: PublicPrivate.PrivateClosedEnum // expected-error {{'PrivateClosedEnum' is not a member type of struct 'AccessSpecifiers.PublicPrivate'}}
var privateOpenEnumVar: PublicPrivate.PrivateOpenEnum // expected-error {{'PrivateOpenEnum' is not a member type of struct 'AccessSpecifiers.PublicPrivate'}}
var privateFlagEnumVar: PublicPrivate.PrivateFlagEnum // expected-error {{'PrivateFlagEnum' is not a member type of struct 'AccessSpecifiers.PublicPrivate'}}
2 changes: 1 addition & 1 deletion test/Sema/diag_module_conflict_with_type.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import SecondModule

public func f(_ x: ModuleType.MyStruct) {}

// CHECK: error: 'MyStruct' is not a member type of 'ModuleType'
// CHECK: error: 'MyStruct' is not a member type of struct 'SecondModule.ModuleType'
// CHECK: SecondModule.ModuleType:1:15: note: 'ModuleType' declared here
// CHECK: public struct ModuleType {
// CHECK: ^
2 changes: 1 addition & 1 deletion test/decl/nested/type_in_function.swift
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ struct OuterGenericStruct<A> {
func genericFunction<T>(t: T) {
class First : Second<T>.UnknownType { }
// expected-error@-1 {{type 'First' cannot be nested in generic function 'genericFunction(t:)'}}
// expected-error@-2 {{'UnknownType' is not a member type of 'Second<T>'}}
// expected-error@-2 {{'UnknownType' is not a member type of generic class 'type_in_function.Second<T>'}}
class Second<T> : Second { } // expected-note{{'Second' declared here}}
// expected-error@-1 {{type 'Second' cannot be nested in generic function 'genericFunction(t:)'}}
// expected-error@-2 {{'Second' inherits from itself}}
Expand Down
4 changes: 2 additions & 2 deletions test/decl/protocol/recursive_requirement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ struct Epsilon<T: Alpha, // expected-note{{conformance constraint 'U': 'Gamma' i
// expected-warning@-1{{redundant conformance constraint 'T': 'Alpha'}}
U: Gamma> // expected-warning{{redundant conformance constraint 'U': 'Gamma'}}
// expected-note@-1{{conformance constraint 'T': 'Alpha' implied here}}
where T.Beta == U, // expected-error{{'Beta' is not a member type of 'T'}}
U.Delta == T {} // expected-error{{'Delta' is not a member type of 'U'}}
where T.Beta == U, // expected-error{{'Beta' is not a member type of type 'T'}}
U.Delta == T {} // expected-error{{'Delta' is not a member type of type 'U'}}

// -----

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,10 +184,10 @@ struct NonSynthesizedClass : Codable { // expected-note 4 {{'NonSynthesizedClass

// Qualified type lookup should clearly fail -- we shouldn't get a synthesized
// type here.
public func qualifiedFoo(_ key: NonSynthesizedClass.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of 'NonSynthesizedClass'}}
internal func qualifiedBar(_ key: NonSynthesizedClass.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of 'NonSynthesizedClass'}}
fileprivate func qualfiedBaz(_ key: NonSynthesizedClass.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of 'NonSynthesizedClass'}}
private func qualifiedQux(_ key: NonSynthesizedClass.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of 'NonSynthesizedClass'}}
public func qualifiedFoo(_ key: NonSynthesizedClass.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of struct 'class_codable_member_type_lookup.NonSynthesizedClass'}}
internal func qualifiedBar(_ key: NonSynthesizedClass.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of struct 'class_codable_member_type_lookup.NonSynthesizedClass'}}
fileprivate func qualfiedBaz(_ key: NonSynthesizedClass.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of struct 'class_codable_member_type_lookup.NonSynthesizedClass'}}
private func qualifiedQux(_ key: NonSynthesizedClass.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of struct 'class_codable_member_type_lookup.NonSynthesizedClass'}}

// Unqualified lookups should find the public top-level CodingKeys type.
public func unqualifiedFoo(_ key: CodingKeys) { print(CodingKeys.topLevel) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,10 +184,10 @@ struct NonSynthesizedStruct : Codable { // expected-note 4 {{'NonSynthesizedStru

// Qualified type lookup should clearly fail -- we shouldn't get a synthesized
// type here.
public func qualifiedFoo(_ key: NonSynthesizedStruct.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of 'NonSynthesizedStruct'}}
internal func qualifiedBar(_ key: NonSynthesizedStruct.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of 'NonSynthesizedStruct'}}
fileprivate func qualfiedBaz(_ key: NonSynthesizedStruct.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of 'NonSynthesizedStruct'}}
private func qualifiedQux(_ key: NonSynthesizedStruct.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of 'NonSynthesizedStruct'}}
public func qualifiedFoo(_ key: NonSynthesizedStruct.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of struct 'struct_codable_member_type_lookup.NonSynthesizedStruct'}}
internal func qualifiedBar(_ key: NonSynthesizedStruct.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of struct 'struct_codable_member_type_lookup.NonSynthesizedStruct'}}
fileprivate func qualfiedBaz(_ key: NonSynthesizedStruct.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of struct 'struct_codable_member_type_lookup.NonSynthesizedStruct'}}
private func qualifiedQux(_ key: NonSynthesizedStruct.CodingKeys) {} // expected-error {{'CodingKeys' is not a member type of struct 'struct_codable_member_type_lookup.NonSynthesizedStruct'}}

// Unqualified lookups should find the public top-level CodingKeys type.
public func unqualifiedFoo(_ key: CodingKeys) { print(CodingKeys.topLevel) }
Expand Down
Loading