Skip to content

Commit c8ea08d

Browse files
[Sema] Creating ambiguous base for none member warning diagnostic as a fix
1 parent 3189d5c commit c8ea08d

File tree

4 files changed

+123
-0
lines changed

4 files changed

+123
-0
lines changed

include/swift/Sema/CSFix.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,10 @@ enum class FixKind : uint8_t {
289289
/// Treat empty and single-element array literals as if they were incomplete
290290
/// dictionary literals when used as such.
291291
TreatArrayLiteralAsDictionary,
292+
293+
/// Explicitly specify the type to disambiguate between possible member base
294+
/// types.
295+
SpecifyBaseTypeForOptionalUnresolvedMember,
292296
};
293297

294298
class ConstraintFix {
@@ -2143,6 +2147,29 @@ class AllowRefToInvalidDecl final : public ConstraintFix {
21432147
ConstraintLocator *locator);
21442148
};
21452149

2150+
class SpecifyBaseTypeForOptionalUnresolvedMember final : public ConstraintFix {
2151+
SpecifyBaseTypeForOptionalUnresolvedMember(ConstraintSystem &cs,
2152+
DeclNameRef memberName,
2153+
ConstraintLocator *locator)
2154+
: ConstraintFix(cs, FixKind::SpecifyBaseTypeForOptionalUnresolvedMember,
2155+
locator, /*isWarning=*/true),
2156+
MemberName(memberName) {}
2157+
DeclNameRef MemberName;
2158+
2159+
public:
2160+
std::string getName() const override {
2161+
const auto name = MemberName.getBaseName();
2162+
return "specify unresolved member optional base type explicitly '" +
2163+
name.userFacingName().str() + "'";
2164+
}
2165+
2166+
bool diagnose(const Solution &solution, bool asNote = false) const override;
2167+
2168+
static SpecifyBaseTypeForOptionalUnresolvedMember *
2169+
create(ConstraintSystem &cs, DeclNameRef memberName,
2170+
ConstraintLocator *locator);
2171+
};
2172+
21462173
} // end namespace constraints
21472174
} // end namespace swift
21482175

lib/Sema/CSDiagnostics.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7095,3 +7095,55 @@ bool InvalidReturnInResultBuilderBody::diagnoseAsError() {
70957095

70967096
return true;
70977097
}
7098+
7099+
bool MemberMissingExplicitBaseTypeFailure::diagnoseAsError() {
7100+
auto UME = castToExpr<UnresolvedMemberExpr>(getAnchor());
7101+
auto memberName = UME->getName().getBaseIdentifier().str();
7102+
auto &DE = getASTContext().Diags;
7103+
auto &solution = getSolution();
7104+
7105+
auto selected = solution.getOverloadChoice(getLocator());
7106+
auto baseType =
7107+
resolveType(selected.choice.getBaseType()->getMetatypeInstanceType());
7108+
7109+
SmallVector<Type, 4> optionals;
7110+
auto baseTyUnwrapped = baseType->lookThroughAllOptionalTypes(optionals);
7111+
7112+
if (!optionals.empty()) {
7113+
auto baseTyName = baseType->getCanonicalType().getString();
7114+
auto baseTyUnwrappedName = baseTyUnwrapped->getString();
7115+
auto loc = UME->getLoc();
7116+
auto startLoc = UME->getStartLoc();
7117+
7118+
DE.diagnoseWithNotes(
7119+
DE.diagnose(loc, diag::optional_ambiguous_case_ref, baseTyName,
7120+
baseTyUnwrappedName, memberName),
7121+
[&]() {
7122+
DE.diagnose(UME->getDotLoc(), diag::optional_fixit_ambiguous_case_ref)
7123+
.fixItInsert(startLoc, "Optional");
7124+
DE.diagnose(UME->getDotLoc(),
7125+
diag::type_fixit_optional_ambiguous_case_ref,
7126+
baseTyUnwrappedName, memberName)
7127+
.fixItInsert(startLoc, baseTyUnwrappedName);
7128+
});
7129+
} else {
7130+
auto baseTypeName = baseType->getCanonicalType().getString();
7131+
auto baseOptionalTypeName =
7132+
OptionalType::get(baseType)->getCanonicalType().getString();
7133+
7134+
DE.diagnoseWithNotes(
7135+
DE.diagnose(UME->getLoc(), diag::optional_ambiguous_case_ref,
7136+
baseTypeName, baseOptionalTypeName, memberName),
7137+
[&]() {
7138+
DE.diagnose(UME->getDotLoc(),
7139+
diag::type_fixit_optional_ambiguous_case_ref,
7140+
baseOptionalTypeName, memberName)
7141+
.fixItInsert(UME->getDotLoc(), baseOptionalTypeName);
7142+
DE.diagnose(UME->getDotLoc(),
7143+
diag::type_fixit_optional_ambiguous_case_ref,
7144+
baseTypeName, memberName)
7145+
.fixItInsert(UME->getDotLoc(), baseTypeName);
7146+
});
7147+
}
7148+
return true;
7149+
}

lib/Sema/CSDiagnostics.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2352,6 +2352,35 @@ class InvalidReturnInResultBuilderBody final : public FailureDiagnostic {
23522352
bool diagnoseAsError() override;
23532353
};
23542354

2355+
/// Diagnose if the base type is optional, we're referring to a nominal
2356+
/// type member via the dot syntax and the member name matches
2357+
/// Optional<T>.{member_name} or an unresolved `.none` inferred as a static
2358+
/// non-optional member base but could be an Optional<T>.none. So we enforce
2359+
/// explicit type annotation to avoid ambiguity.
2360+
///
2361+
/// \code
2362+
/// enum Enum<T> {
2363+
/// case bar
2364+
/// static var none: Enum<Int> { .bar }
2365+
/// }
2366+
/// let _: Enum<Int>? = .none // Base inferred as Optional.none, suggest
2367+
/// // explicit type.
2368+
/// let _: Enum? = .none // Base inferred as static member Enum<Int>.none,
2369+
/// // emit warning suggesting explicit type.
2370+
/// let _: Enum = .none // Ok
2371+
/// \endcode
2372+
class MemberMissingExplicitBaseTypeFailure final : public FailureDiagnostic {
2373+
DeclNameRef Member;
2374+
2375+
public:
2376+
MemberMissingExplicitBaseTypeFailure(const Solution &solution,
2377+
DeclNameRef member,
2378+
ConstraintLocator *locator)
2379+
: FailureDiagnostic(solution, locator), Member(member) {}
2380+
2381+
bool diagnoseAsError() override;
2382+
};
2383+
23552384
} // end namespace constraints
23562385
} // end namespace swift
23572386

lib/Sema/CSFix.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1673,3 +1673,18 @@ IgnoreResultBuilderWithReturnStmts::create(ConstraintSystem &cs, Type builderTy,
16731673
return new (cs.getAllocator())
16741674
IgnoreResultBuilderWithReturnStmts(cs, builderTy, locator);
16751675
}
1676+
1677+
bool SpecifyBaseTypeForOptionalUnresolvedMember::diagnose(
1678+
const Solution &solution, bool asNote) const {
1679+
MemberMissingExplicitBaseTypeFailure failure(solution, MemberName,
1680+
getLocator());
1681+
return failure.diagnose(asNote);
1682+
}
1683+
1684+
SpecifyBaseTypeForOptionalUnresolvedMember *
1685+
SpecifyBaseTypeForOptionalUnresolvedMember::create(ConstraintSystem &cs,
1686+
DeclNameRef memberName,
1687+
ConstraintLocator *locator) {
1688+
return new (cs.getAllocator())
1689+
SpecifyBaseTypeForOptionalUnresolvedMember(cs, memberName, locator);
1690+
}

0 commit comments

Comments
 (0)