Skip to content

[Sema/Index] Resolve #keyPath components so they get handled by indexing, semantic highlighting, etc #33320

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
Aug 6, 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
11 changes: 11 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -4657,13 +4657,24 @@ ERROR(availability_decl_unavailable, none,
"%select{ in %3|}2%select{|: %4}4",
(unsigned, DeclName, bool, StringRef, StringRef))

WARNING(availability_decl_unavailable_warn, none,
"%select{getter for |setter for |}0%1 is unavailable"
Copy link
Contributor

Choose a reason for hiding this comment

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

Please add both of these new diagnostics to localization/diagnostics/en.yaml

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh neat! Will do.

"%select{ in %3|}2%select{|: %4}4",
(unsigned, DeclName, bool, StringRef, StringRef))

#define REPLACEMENT_DECL_KIND_SELECT "select{| instance method| property}"
ERROR(availability_decl_unavailable_rename, none,
"%select{getter for |setter for |}0%1 has been "
"%select{renamed to|replaced by}2%" REPLACEMENT_DECL_KIND_SELECT "3 "
"'%4'%select{|: %5}5",
(unsigned, DeclName, bool, unsigned, StringRef, StringRef))

WARNING(availability_decl_unavailable_rename_warn, none,
"%select{getter for |setter for |}0%1 has been "
"%select{renamed to|replaced by}2%" REPLACEMENT_DECL_KIND_SELECT "3 "
"'%4'%select{|: %5}5",
(unsigned, DeclName, bool, unsigned, StringRef, StringRef))

NOTE(availability_marked_unavailable, none,
"%select{getter for |setter for |}0%1 has been explicitly marked "
"unavailable here", (unsigned, DeclName))
Expand Down
18 changes: 18 additions & 0 deletions include/swift/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -5259,6 +5259,7 @@ class KeyPathExpr : public Expr {
OptionalWrap,
Identity,
TupleElement,
DictionaryKey,
};

private:
Expand Down Expand Up @@ -5367,6 +5368,16 @@ class KeyPathExpr : public Expr {
propertyType,
loc);
}

/// Create a component for a dictionary key (#keyPath only).
static Component forDictionaryKey(DeclNameRef UnresolvedName,
Type valueType,
SourceLoc loc) {
return Component(nullptr, UnresolvedName, nullptr, {}, {},
Kind::DictionaryKey,
valueType,
loc);
}

/// Create a component for a subscript.
static Component forSubscript(ASTContext &ctx,
Expand Down Expand Up @@ -5457,6 +5468,7 @@ class KeyPathExpr : public Expr {
case Kind::Property:
case Kind::Identity:
case Kind::TupleElement:
case Kind::DictionaryKey:
return true;

case Kind::UnresolvedSubscript:
Expand All @@ -5481,6 +5493,7 @@ class KeyPathExpr : public Expr {
case Kind::Property:
case Kind::Identity:
case Kind::TupleElement:
case Kind::DictionaryKey:
return nullptr;
}
llvm_unreachable("unhandled kind");
Expand All @@ -5500,6 +5513,7 @@ class KeyPathExpr : public Expr {
case Kind::Property:
case Kind::Identity:
case Kind::TupleElement:
case Kind::DictionaryKey:
llvm_unreachable("no subscript labels for this kind");
}
llvm_unreachable("unhandled kind");
Expand All @@ -5522,6 +5536,7 @@ class KeyPathExpr : public Expr {
case Kind::Property:
case Kind::Identity:
case Kind::TupleElement:
case Kind::DictionaryKey:
return {};
}
llvm_unreachable("unhandled kind");
Expand All @@ -5533,6 +5548,7 @@ class KeyPathExpr : public Expr {
DeclNameRef getUnresolvedDeclName() const {
switch (getKind()) {
case Kind::UnresolvedProperty:
case Kind::DictionaryKey:
return Decl.UnresolvedName;

case Kind::Invalid:
Expand Down Expand Up @@ -5563,6 +5579,7 @@ class KeyPathExpr : public Expr {
case Kind::OptionalForce:
case Kind::Identity:
case Kind::TupleElement:
case Kind::DictionaryKey:
llvm_unreachable("no decl ref for this kind");
}
llvm_unreachable("unhandled kind");
Expand All @@ -5582,6 +5599,7 @@ class KeyPathExpr : public Expr {
case Kind::Identity:
case Kind::Property:
case Kind::Subscript:
case Kind::DictionaryKey:
llvm_unreachable("no field number for this kind");
}
llvm_unreachable("unhandled kind");
Expand Down
5 changes: 5 additions & 0 deletions lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2829,6 +2829,11 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
PrintWithColorRAII(OS, DiscriminatorColor)
<< "#" << component.getTupleIndex();
break;
case KeyPathExpr::Component::Kind::DictionaryKey:
PrintWithColorRAII(OS, ASTNodeColor) << "dict_key";
PrintWithColorRAII(OS, IdentifierColor)
<< " key='" << component.getUnresolvedDeclName() << "'";
break;
}
PrintWithColorRAII(OS, TypeColor)
<< " type='" << GetTypeOfKeyPathComponent(E, i) << "'";
Expand Down
1 change: 1 addition & 0 deletions lib/AST/ASTWalker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1133,6 +1133,7 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
case KeyPathExpr::Component::Kind::Invalid:
case KeyPathExpr::Component::Kind::Identity:
case KeyPathExpr::Component::Kind::TupleElement:
case KeyPathExpr::Component::Kind::DictionaryKey:
// No subexpr to visit.
break;
}
Expand Down
1 change: 1 addition & 0 deletions lib/AST/Expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2383,6 +2383,7 @@ void KeyPathExpr::Component::setSubscriptIndexHashableConformances(
case Kind::Property:
case Kind::Identity:
case Kind::TupleElement:
case Kind::DictionaryKey:
llvm_unreachable("no hashable conformances for this kind");
}
}
Expand Down
1 change: 1 addition & 0 deletions lib/IDE/SourceEntityWalker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,7 @@ std::pair<bool, Expr *> SemaAnnotator::walkToExprPre(Expr *E) {
case KeyPathExpr::Component::Kind::OptionalWrap:
case KeyPathExpr::Component::Kind::OptionalForce:
case KeyPathExpr::Component::Kind::Identity:
case KeyPathExpr::Component::Kind::DictionaryKey:
break;
}
}
Expand Down
5 changes: 5 additions & 0 deletions lib/SILGen/SILGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3734,6 +3734,11 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) {
case KeyPathExpr::Component::Kind::UnresolvedProperty:
case KeyPathExpr::Component::Kind::UnresolvedSubscript:
llvm_unreachable("not resolved");
break;

case KeyPathExpr::Component::Kind::DictionaryKey:
llvm_unreachable("DictionaryKey only valid in #keyPath");
break;
}
}

Expand Down
10 changes: 8 additions & 2 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,9 @@ static bool buildObjCKeyPathString(KeyPathExpr *E,
// Don't bother building the key path string if the key path didn't even
// resolve.
return false;
case KeyPathExpr::Component::Kind::DictionaryKey:
llvm_unreachable("DictionaryKey only valid in #keyPath expressions.");
return false;
}
}

Expand Down Expand Up @@ -4690,6 +4693,10 @@ namespace {
case KeyPathExpr::Component::Kind::OptionalWrap:
case KeyPathExpr::Component::Kind::TupleElement:
llvm_unreachable("already resolved");
break;
case KeyPathExpr::Component::Kind::DictionaryKey:
llvm_unreachable("DictionaryKey only valid in #keyPath");
break;
}

// Update "componentTy" with the result type of the last component.
Expand Down Expand Up @@ -7745,9 +7752,8 @@ namespace {
componentType = solution.simplifyType(cs.getType(kp, i));
assert(!componentType->hasTypeVariable() &&
"Should not write type variable into key-path component");
kp->getMutableComponents()[i].setComponentType(componentType);
}

kp->getMutableComponents()[i].setComponentType(componentType);
}
}

Expand Down
3 changes: 3 additions & 0 deletions lib/Sema/CSGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3487,6 +3487,9 @@ namespace {
}
case KeyPathExpr::Component::Kind::Identity:
continue;
case KeyPathExpr::Component::Kind::DictionaryKey:
llvm_unreachable("DictionaryKey only valid in #keyPath");
break;
}

// By now, `base` is the result type of this component. Set it in the
Expand Down
3 changes: 3 additions & 0 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8242,6 +8242,9 @@ ConstraintSystem::simplifyKeyPathConstraint(
case KeyPathExpr::Component::Kind::TupleElement:
llvm_unreachable("not implemented");
break;
case KeyPathExpr::Component::Kind::DictionaryKey:
llvm_unreachable("DictionaryKey only valid in #keyPath");
break;
}
}

Expand Down
1 change: 1 addition & 0 deletions lib/Sema/ConstraintSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ ConstraintLocator *ConstraintSystem::getCalleeLocator(
case ComponentKind::OptionalChain:
case ComponentKind::OptionalWrap:
case ComponentKind::Identity:
case ComponentKind::DictionaryKey:
// These components don't have any callee associated, so just continue.
break;
}
Expand Down
36 changes: 28 additions & 8 deletions lib/Sema/TypeCheckAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2067,6 +2067,7 @@ void swift::diagnoseUnavailableOverride(ValueDecl *override,

diagnoseExplicitUnavailability(base, override->getLoc(),
override->getDeclContext(),
/*Flags*/None,
[&](InFlightDiagnostic &diag) {
ParsedDeclName parsedName = parseDeclName(attr->Rename);
if (!parsedName || parsedName.isPropertyAccessor() ||
Expand Down Expand Up @@ -2097,10 +2098,11 @@ void swift::diagnoseUnavailableOverride(ValueDecl *override,
/// Emit a diagnostic for references to declarations that have been
/// marked as unavailable, either through "unavailable" or "obsoleted:".
bool swift::diagnoseExplicitUnavailability(const ValueDecl *D,
SourceRange R,
const DeclContext *DC,
const ApplyExpr *call) {
return diagnoseExplicitUnavailability(D, R, DC,
SourceRange R,
const DeclContext *DC,
const ApplyExpr *call,
DeclAvailabilityFlags Flags) {
return diagnoseExplicitUnavailability(D, R, DC, Flags,
[=](InFlightDiagnostic &diag) {
fixItAvailableAttrRename(diag, R, D, AvailableAttr::isUnavailable(D),
call);
Expand Down Expand Up @@ -2172,6 +2174,7 @@ bool swift::diagnoseExplicitUnavailability(
const ValueDecl *D,
SourceRange R,
const DeclContext *DC,
DeclAvailabilityFlags Flags,
llvm::function_ref<void(InFlightDiagnostic &)> attachRenameFixIts) {
auto *Attr = AvailableAttr::isUnavailable(D);
if (!Attr)
Expand Down Expand Up @@ -2229,6 +2232,14 @@ bool swift::diagnoseExplicitUnavailability(
break;
}

// TODO: Consider removing this.
// ObjC keypaths components weren't checked previously, so errors are demoted
// to warnings to avoid source breakage. In some cases unavailable or
// obsolete decls still map to valid ObjC runtime names, so behave correctly
// at runtime, even though their use would produce an error outside of a
// #keyPath expression.
bool warnInObjCKeyPath = Flags.contains(DeclAvailabilityFlag::ForObjCKeyPath);

if (!Attr->Rename.empty()) {
SmallString<32> newNameBuf;
Optional<ReplacementDeclKind> replaceKind =
Expand All @@ -2238,7 +2249,9 @@ bool swift::diagnoseExplicitUnavailability(
StringRef newName = replaceKind ? newNameBuf.str() : Attr->Rename;
EncodedDiagnosticMessage EncodedMessage(Attr->Message);
auto diag =
diags.diagnose(Loc, diag::availability_decl_unavailable_rename,
diags.diagnose(Loc, warnInObjCKeyPath
? diag::availability_decl_unavailable_rename_warn
: diag::availability_decl_unavailable_rename,
RawAccessorKind, Name, replaceKind.hasValue(),
rawReplaceKind, newName, EncodedMessage.Message);
attachRenameFixIts(diag);
Expand All @@ -2253,7 +2266,9 @@ bool swift::diagnoseExplicitUnavailability(
} else {
EncodedDiagnosticMessage EncodedMessage(Attr->Message);
diags
.diagnose(Loc, diag::availability_decl_unavailable, RawAccessorKind,
.diagnose(Loc, warnInObjCKeyPath
? diag::availability_decl_unavailable_warn
: diag::availability_decl_unavailable, RawAccessorKind,
Name, platform.empty(), platform, EncodedMessage.Message)
.highlight(R);
}
Expand Down Expand Up @@ -2501,14 +2516,18 @@ class AvailabilityWalker : public ASTWalker {
/// Walk a keypath expression, checking all of its components for
/// availability.
void maybeDiagKeyPath(KeyPathExpr *KP) {
auto flags = DeclAvailabilityFlags();
if (KP->isObjC())
flags = DeclAvailabilityFlag::ForObjCKeyPath;

for (auto &component : KP->getComponents()) {
switch (component.getKind()) {
case KeyPathExpr::Component::Kind::Property:
case KeyPathExpr::Component::Kind::Subscript: {
auto *decl = component.getDeclRef().getDecl();
auto loc = component.getLoc();
SourceRange range(loc, loc);
diagAvailability(decl, range, nullptr);
diagAvailability(decl, range, nullptr, flags);
break;
}

Expand All @@ -2522,6 +2541,7 @@ class AvailabilityWalker : public ASTWalker {
case KeyPathExpr::Component::Kind::OptionalWrap:
case KeyPathExpr::Component::Kind::OptionalForce:
case KeyPathExpr::Component::Kind::Identity:
case KeyPathExpr::Component::Kind::DictionaryKey:
break;
}
}
Expand Down Expand Up @@ -2627,7 +2647,7 @@ AvailabilityWalker::diagAvailability(ConcreteDeclRef declRef, SourceRange R,
if (TypeChecker::diagnoseInlinableDeclRef(R.Start, declRef, DC, FragileKind))
return true;

if (diagnoseExplicitUnavailability(D, R, DC, call))
if (diagnoseExplicitUnavailability(D, R, DC, call, Flags))
return true;

// Make sure not to diagnose an accessor's deprecation if we already
Expand Down
8 changes: 7 additions & 1 deletion lib/Sema/TypeCheckAvailability.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ enum class DeclAvailabilityFlag : uint8_t {
/// Do not diagnose uses of declarations in versions before they were
/// introduced. Used to work around availability-checker bugs.
AllowPotentiallyUnavailable = 1 << 3,

/// If an error diagnostic would normally be emitted, demote the error to a
/// warning. Used for ObjC key path components.
ForObjCKeyPath = 1 << 4
};
using DeclAvailabilityFlags = OptionSet<DeclAvailabilityFlag>;

Expand All @@ -70,14 +74,16 @@ void diagnoseUnavailableOverride(ValueDecl *override,
bool diagnoseExplicitUnavailability(const ValueDecl *D,
SourceRange R,
const DeclContext *DC,
const ApplyExpr *call);
const ApplyExpr *call,
DeclAvailabilityFlags Flags = None);

/// Emit a diagnostic for references to declarations that have been
/// marked as unavailable, either through "unavailable" or "obsoleted:".
bool diagnoseExplicitUnavailability(
const ValueDecl *D,
SourceRange R,
const DeclContext *DC,
DeclAvailabilityFlags Flags,
llvm::function_ref<void(InFlightDiagnostic &)> attachRenameFixIts);

/// Check if \p decl has a introduction version required by -require-explicit-availability
Expand Down
18 changes: 15 additions & 3 deletions lib/Sema/TypeCheckCodeCompletion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -621,9 +621,21 @@ static Optional<Type> getTypeOfCompletionContextExpr(

case CompletionTypeCheckKind::KeyPath:
referencedDecl = nullptr;
if (auto keyPath = dyn_cast<KeyPathExpr>(parsedExpr))
return TypeChecker::checkObjCKeyPathExpr(DC, keyPath,
/*requireResultType=*/true);
if (auto keyPath = dyn_cast<KeyPathExpr>(parsedExpr)) {
auto components = keyPath->getComponents();
if (!components.empty()) {
auto &last = components.back();
if (last.isResolved()) {
if (last.getKind() == KeyPathExpr::Component::Kind::Property)
referencedDecl = last.getDeclRef();
Type lookupTy = last.getComponentType();
ASTContext &Ctx = DC->getASTContext();
if (auto bridgedClass = Ctx.getBridgedToObjC(DC, lookupTy))
return bridgedClass;
return lookupTy;
}
}
}

return None;
}
Expand Down
Loading