QoI: Typo correction for mistyped associated types.

Swift SVN r22678
This commit is contained in:
Doug Gregor
2014-10-10 22:11:15 +00:00
parent 7d6ca8c2b8
commit 09688472d2
6 changed files with 175 additions and 10 deletions

View File

@@ -264,6 +264,12 @@ public:
/// \returns true if an error occurred, false otherwise.
bool inferRequirements(Pattern *pattern);
/// Finalize the set of requirements, performing any remaining checking
/// required before generating archetypes.
///
/// \returns true if an error occurs, false otherwse.
bool finalize(SourceLoc loc);
/// \brief Resolve the given type to the potential archetype it names.
///
/// This routine will synthesize nested types as required to refer to a
@@ -384,6 +390,10 @@ class ArchetypeBuilder::PotentialArchetype {
/// \brief Recursively conforms to itself.
unsigned IsRecursive : 1;
/// Whether this potential archetype is invalid, e.g., because it could not
/// be resolved.
unsigned Invalid : 1;
/// The references to this nested type that occur in the source code
/// that were unresolved (at least at some point).
llvm::TinyPtrVector<ComponentIdentTypeRepr *> UnresolvedReferences;
@@ -392,7 +402,7 @@ class ArchetypeBuilder::PotentialArchetype {
/// associated type.
PotentialArchetype(PotentialArchetype *Parent, Identifier Name)
: ParentOrParam(Parent), NameOrAssociatedType(Name), Representative(this),
IsRecursive(false)
IsRecursive(false), Invalid(false)
{
assert(Parent != nullptr && "Not an associated type?");
}
@@ -400,7 +410,7 @@ class ArchetypeBuilder::PotentialArchetype {
/// \brief Construct a new potential archetype for an associated type.
PotentialArchetype(PotentialArchetype *Parent, AssociatedTypeDecl *AssocType)
: ParentOrParam(Parent), NameOrAssociatedType(AssocType),
Representative(this), IsRecursive(false)
Representative(this), IsRecursive(false), Invalid(false)
{
assert(Parent != nullptr && "Not an associated type?");
}
@@ -410,7 +420,8 @@ class ArchetypeBuilder::PotentialArchetype {
ProtocolDecl *RootProtocol,
Identifier Name)
: ParentOrParam(GenericParam), RootProtocol(RootProtocol),
NameOrAssociatedType(Name), Representative(this), IsRecursive(false) { }
NameOrAssociatedType(Name), Representative(this), IsRecursive(false),
Invalid(false) { }
/// \brief Recursively build the full name.
void buildFullName(bool forDebug, SmallVectorImpl<char> &result) const;
@@ -522,9 +533,13 @@ public:
return getGenericParam() && !isConcreteType();
}
void setIsRecursive() { this->IsRecursive = true; }
bool isRecursive() { return this->IsRecursive; }
void setIsRecursive() { IsRecursive = true; }
bool isRecursive() { return IsRecursive; }
bool isInvalid() { return Invalid; }
void setInvalid() { Invalid = true; }
/// Return the list of unresolved references to this associated type.
ArrayRef<ComponentIdentTypeRepr *> getUnresolvedReferences() const {
return UnresolvedReferences;

View File

@@ -127,6 +127,9 @@ ERROR(ambiguous_type_base,sema_nb,none,
"%0 is ambiguous for type lookup in this context", (Identifier))
ERROR(invalid_member_type,sema_nb,none,
"%0 is not a member type of %1", (Identifier, Type))
ERROR(invalid_member_type_suggest,sema_nb,none,
"%0 does not have a member type named %1; did you mean %2?",
(Type, Identifier, Identifier))
ERROR(ambiguous_member_type,sema_nb,none,
"ambiguous type name %0 in %1", (Identifier, Type))
ERROR(no_module_type,sema_nb,none,

View File

@@ -313,8 +313,6 @@ bool ArchetypeBuilder::PotentialArchetype::addConformance(
// Check whether any associated types in this protocol resolve
// nested types of this potential archetype.
// FIXME: Eventually, will need to add more same-type requirements
// here as well.
for (auto member : proto->getMembers()) {
auto assocType = dyn_cast<AssociatedTypeDecl>(member);
if (!assocType)
@@ -791,7 +789,7 @@ bool ArchetypeBuilder::addSameTypeRequirementBetweenArchetypes(
// associated type.
unsigned T1Depth = T1->getNestingDepth();
unsigned T2Depth = T2->getNestingDepth();
if (T2Depth < T1Depth)
if (T2Depth < T1Depth || (T1->isInvalid() && !T2->isInvalid()))
std::swap(T1, T2);
// Don't allow two generic parameters to be equivalent, because then we
@@ -1151,6 +1149,108 @@ bool ArchetypeBuilder::inferRequirements(Pattern *pattern) {
return walker.hadError();
}
/// Perform typo correction on the given nested type, producing the
/// corrected name (if successful).
static Identifier typoCorrectNestedType(
ArchetypeBuilder::PotentialArchetype *pa) {
StringRef name = pa->getName().str();
// Look through all of the associated types of all of the protocols
// to which the parent conforms.
llvm::SmallVector<Identifier, 2> bestMatches;
unsigned bestEditDistance = 0;
unsigned maxScore = (name.size() + 1) / 3;
for (const auto &conforms : pa->getParent()->getConformsTo()) {
auto proto = conforms.first;
for (auto member : proto->getMembers()) {
auto assocType = dyn_cast<AssociatedTypeDecl>(member);
if (!assocType)
continue;
unsigned dist = name.edit_distance(assocType->getName().str(),
/*allowReplacements=*/true,
maxScore);
assert(dist > 0 && "nested type should have matched associated type");
if (bestEditDistance == 0 || dist == bestEditDistance) {
bestEditDistance = dist;
maxScore = bestEditDistance;
bestMatches.push_back(assocType->getName());
} else if (dist < bestEditDistance) {
bestEditDistance = dist;
maxScore = bestEditDistance;
bestMatches.clear();
bestMatches.push_back(assocType->getName());
}
}
}
// If we didn't find any matches at all, fail.
if (bestMatches.empty())
return Identifier();
// Make sure that we didn't find more than one match at the best
// edit distance.
for (auto other : llvm::makeArrayRef(bestMatches).slice(1)) {
if (other != bestMatches.front())
return Identifier();
}
return bestMatches.front();
}
bool ArchetypeBuilder::finalize(SourceLoc loc) {
bool invalid = false;
// If any nested types remain unresolved, produce diagnostics.
if (Impl->NumUnresolvedNestedTypes > 0) {
visitPotentialArchetypes([&](PotentialArchetype *pa) {
// We only care about nested types that haven't been resolved.
if (pa->getParent() == nullptr || pa->getResolvedAssociatedType() ||
pa->getRepresentative() != pa ||
/* FIXME: Should be able to handle this earlier */pa->getSuperclass())
return;
assert(!pa->getUnresolvedReferences().empty() &&
"Missing unresolved references?");
// Try to typo correct to a nested type name.
Identifier correction = typoCorrectNestedType(pa);
SourceLoc diagLoc = pa->getUnresolvedReferences().front()->getIdLoc();
// Typo correction failed; a diagnostic will be emitted later.
if (correction.empty()) {
invalid = true;
return;
}
// Typo correction succeeded; emit Fix-Its and update the
// component ids.
auto diag = Diags.diagnose(diagLoc, diag::invalid_member_type_suggest,
pa->getParent()->getDependentType(*this),
pa->getName(),
correction);
for (auto comp : pa->getUnresolvedReferences()) {
diag.fixItReplace(comp->getIdLoc(), correction.str());
comp->overwriteIdentifier(correction);
}
// Resolve the associated type and merge the potential archetypes.
pa->setInvalid();
auto replacement = pa->getParent()->getNestedType(correction, *this,
nullptr);
pa->resolveAssociatedType(replacement->getResolvedAssociatedType(),
*this);
addSameTypeRequirementBetweenArchetypes(
pa, replacement,
RequirementSource(RequirementSource::Protocol, diagLoc));
});
}
return invalid;
}
ArchetypeType *
ArchetypeBuilder::getArchetype(GenericTypeParamDecl *GenericParam) {
auto known = Impl->PotentialArchetypes.find(
@@ -1226,6 +1326,11 @@ void ArchetypeBuilder::visitPotentialArchetypes(F f) {
template<typename F>
void ArchetypeBuilder::enumerateRequirements(F f) {
visitPotentialArchetypes([&](PotentialArchetype *archetype) {
// Invalid archetypes are never representatives in well-formed or corrected
// signature, so we don't need to visit them.
if (archetype->isInvalid())
return;
// If this is not the representative, produce a same-type
// constraint to the representative.
if (archetype->getRepresentative() != archetype) {

View File

@@ -651,6 +651,9 @@ bool TypeChecker::validateGenericFuncSignature(AbstractFunctionDecl *func) {
if (func->hasType())
return !func->isInvalid();
// Finalize the generic requirements.
(void)builder.finalize(func->getLoc());
// The archetype builder now has all of the requirements, although there might
// still be errors that have not yet been diagnosed. Revert the generic
// function signature and type-check it again, completely.
@@ -821,6 +824,9 @@ GenericSignature *TypeChecker::validateGenericSignature(
invalid = true;
}
// Finalize the generic requirements.
(void)builder.finalize(genericParams->getSourceRange().Start);
// The archetype builder now has all of the requirements, although there might
// still be errors that have not yet been diagnosed. Revert the signature
// and type-check it again, completely.

View File

@@ -576,7 +576,6 @@ resolveIdentTypeComponent(TypeChecker &TC, DeclContext *DC,
// Otherwise, check for an ambiguity.
if (current.is<Module *>() || !current.get<Type>()->isEqual(type)) {
auto currentType = current.get<Type>();
isAmbiguous = true;
break;
}

View File

@@ -0,0 +1,37 @@
// RUN: %swift -parse %s -verify
// RUN: not %swift -parse -debug-generic-signatures %s > %t.dump 2>&1
// RUN: FileCheck -check-prefix CHECK-GENERIC %s < %t.dump
protocol P1 {
typealias Assoc
}
protocol P2 {
typealias AssocP2 : P1
}
protocol P3 { }
protocol P4 { }
// expected-error@+1{{'T' does not have a member type named 'assoc'; did you mean 'Assoc'?}}{{30-35=Assoc}}
func typoAssoc1<T : P1>(x: T.assoc) { }
// expected-error@+1{{'T' does not have a member type named 'assoc'; did you mean 'Assoc'?}}{{40-45=Assoc}}{{51-56=Assoc}}
func typoAssoc2<T : P1, U : P1 where T.assoc == U.assoc>() { }
// expected-error@+2{{'T.AssocP2' does not have a member type named 'assoc'; did you mean 'Assoc'?}}{{27-32=Assoc}}{{50-55=Assoc}}
func typoAssoc3<T : P2, U : P2 where
U.AssocP2.assoc : P3, T.AssocP2.assoc : P4,
T.AssocP2 == U.AssocP2>() { }
// expected-error@+2{{'T' does not have a member type named 'Assocp2'; did you mean 'AssocP2'?}}{{32-39=AssocP2}}
// expected-error@+1{{'T.AssocP2' does not have a member type named 'assoc'; did you mean 'Assoc'?}}{{40-45=Assoc}}
func typoAssoc4<T : P2 where T.Assocp2.assoc : P3>() { }
// CHECK-GENERIC-LABEL: .typoAssoc4()@
// CHECK-NEXT: Requirements:
// CHECK-NEXT: T : P2 [explicit
// CHECK-NEXT: T[.P2].AssocP2 == T.AssocP2 [protocol
// CHECK-NEXT: T[.P2].AssocP2[.P1].Assoc : P3 [explicit
// CHECK-NEXT: Generic signature