Skip to content

Commit 77942d3

Browse files
committed
Tighten up @objc optional near-miss detection heuristics.
The @objc optional requirement near-miss heuristics were too permissive, and could occasionally produce ridiculous results that were nowhere close to a "near" miss. Make the diagnostics more conservative, and fix an issue with an errant sentinel value. Fixes rdar://problem/26380688.
1 parent 76b5afc commit 77942d3

File tree

2 files changed

+26
-13
lines changed

2 files changed

+26
-13
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4479,12 +4479,13 @@ combineBaseNameAndFirstArgument(Identifier baseName,
44794479
/// Compute the scope between two potentially-matching names, which is
44804480
/// effectively the sum of the edit distances between the corresponding
44814481
/// argument labels.
4482-
static unsigned scorePotentiallyMatchingNames(DeclName lhs, DeclName rhs,
4483-
bool isFunc,
4484-
unsigned limit) {
4482+
static Optional<unsigned> scorePotentiallyMatchingNames(DeclName lhs,
4483+
DeclName rhs,
4484+
bool isFunc,
4485+
unsigned limit) {
44854486
// If there are a different number of argument labels, we're done.
44864487
if (lhs.getArgumentNames().size() != rhs.getArgumentNames().size())
4487-
return limit;
4488+
return None;
44884489

44894490
// Score the base name match. If there is a first argument for a
44904491
// function, include its text along with the base name's text.
@@ -4506,14 +4507,14 @@ static unsigned scorePotentiallyMatchingNames(DeclName lhs, DeclName rhs,
45064507

45074508
score = lhsFirstName.edit_distance(rhsFirstName.str(), true, limit);
45084509
}
4509-
if (score >= limit) return limit;
4510+
if (score > limit) return None;
45104511

45114512
// Compute the edit distance between matching argument names.
45124513
for (unsigned i = isFunc ? 1 : 0; i < lhs.getArgumentNames().size(); ++i) {
45134514
score += scoreIdentifiers(lhs.getArgumentNames()[i],
45144515
rhs.getArgumentNames()[i],
45154516
limit - score);
4516-
if (score >= limit) return limit;
4517+
if (score > limit) return None;
45174518
}
45184519

45194520
return score;
@@ -4532,8 +4533,10 @@ static Optional<DeclName> omitNeedlessWords(TypeChecker &tc, ValueDecl *value) {
45324533
}
45334534

45344535
/// Determine the score between two potentially-matching declarations.
4535-
static unsigned scorePotentiallyMatching(TypeChecker &tc, ValueDecl *req,
4536-
ValueDecl *witness, unsigned limit) {
4536+
static Optional<unsigned> scorePotentiallyMatching(TypeChecker &tc,
4537+
ValueDecl *req,
4538+
ValueDecl *witness,
4539+
unsigned limit) {
45374540
DeclName reqName = req->getFullName();
45384541
DeclName witnessName = witness->getFullName();
45394542

@@ -4663,12 +4666,12 @@ static bool shouldWarnAboutPotentialWitness(ValueDecl *req,
46634666
}
46644667

46654668
// If the score is relatively high, don't warn: this is probably
4666-
// unrelated. Allow about one typo for every two properly-typed
4669+
// unrelated. Allow about one typo for every four properly-typed
46674670
// characters, which prevents completely-wacky suggestions in many
46684671
// cases.
46694672
unsigned reqNameLen = getNameLength(req->getFullName());
46704673
unsigned witnessNameLen = getNameLength(witness->getFullName());
4671-
if (score > (std::min(reqNameLen, witnessNameLen) + 1) / 3)
4674+
if (score > (std::min(reqNameLen, witnessNameLen)) / 4)
46724675
return false;
46734676

46744677
return true;
@@ -4878,16 +4881,17 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc,
48784881

48794882
// Score this particular optional requirement.
48804883
auto score = scorePotentiallyMatching(*this, req, value, bestScore);
4884+
if (!score) continue;
48814885

48824886
// If the score is better than the best we've seen, update the best
48834887
// and clear out the list.
4884-
if (score < bestScore) {
4888+
if (*score < bestScore) {
48854889
bestOptionalReqs.clear();
4886-
bestScore = score;
4890+
bestScore = *score;
48874891
}
48884892

48894893
// If this score matches the (possible new) best score, record it.
4890-
if (score == bestScore && bestScore < UINT_MAX)
4894+
if (*score == bestScore)
48914895
bestOptionalReqs.push_back(req);
48924896
}
48934897

test/decl/protocol/conforms/near_miss_objc.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,12 @@ class C10Super {
144144
class C10Sub : C10Super, P8 {
145145
override func foo(nearMatch: Int) { }
146146
}
147+
148+
// Be more strict about near misses than we had previously.
149+
@objc protocol P11 {
150+
@objc optional func foo(wibble: Int)
151+
}
152+
153+
class C11 : P11 {
154+
func f(waggle: Int) { } // no warning
155+
}

0 commit comments

Comments
 (0)