19
19
#include " MiscDiagnostics.h"
20
20
#include " TypeChecker.h"
21
21
#include " swift/Basic/SourceManager.h"
22
+ #include " swift/Basic/StringExtras.h"
22
23
#include " swift/AST/ArchetypeBuilder.h"
23
24
#include " swift/AST/ASTContext.h"
24
25
#include " swift/AST/Decl.h"
@@ -4109,25 +4110,41 @@ void TypeChecker::checkConformance(NormalProtocolConformance *conformance) {
4109
4110
4110
4111
// / Determine the score when trying to match two identifiers together.
4111
4112
static unsigned scoreIdentifiers (Identifier lhs, Identifier rhs,
4112
- unsigned limit, bool isFirstParamOfFunc ) {
4113
+ unsigned limit) {
4113
4114
// Simple case: we have the same identifier.
4114
4115
if (lhs == rhs) return 0 ;
4115
4116
4116
- // One of the identifiers is empty.
4117
- if (lhs.empty () != rhs.empty ()) {
4118
- // Attribute a score of "1" when the first argument of a function is
4119
- // present in one case but absent in the other, because this was a change
4120
- // from Swift 2 to Swift 3.
4121
- if (isFirstParamOfFunc) return 1 ;
4122
-
4123
- // Otherwise, use the length of the non-empty identifier.
4117
+ // One of the identifiers is empty. Use the length of the non-empty
4118
+ // identifier.
4119
+ if (lhs.empty () != rhs.empty ())
4124
4120
return lhs.empty () ? rhs.str ().size () : lhs.str ().size ();
4125
- }
4126
4121
4127
4122
// Compute the edit distance between the two names.
4128
4123
return lhs.str ().edit_distance (rhs.str (), true , limit);
4129
4124
}
4130
4125
4126
+ // / Combine the given base name and first argument label into a single
4127
+ // / name.
4128
+ static StringRef
4129
+ combineBaseNameAndFirstArgument (Identifier baseName,
4130
+ Identifier firstArgName,
4131
+ SmallVectorImpl<char > &scratch) {
4132
+ // Handle cases where one or the other name is empty.
4133
+ if (baseName.empty ()) {
4134
+ if (firstArgName.empty ()) return " " ;
4135
+ return firstArgName.str ();
4136
+ }
4137
+
4138
+ if (firstArgName.empty ())
4139
+ return baseName.str ();
4140
+
4141
+ // Append the first argument name to the base name.
4142
+ scratch.clear ();
4143
+ scratch.append (baseName.str ().begin (), baseName.str ().end ());
4144
+ camel_case::appendSentenceCase (scratch, firstArgName.str ());
4145
+ return StringRef (scratch.data (), scratch.size ());
4146
+ }
4147
+
4131
4148
// / Compute the scope between two potentially-matching names, which is
4132
4149
// / effectively the sum of the edit distances between the corresponding
4133
4150
// / argument labels.
@@ -4138,17 +4155,33 @@ static unsigned scorePotentiallyMatchingNames(DeclName lhs, DeclName rhs,
4138
4155
if (lhs.getArgumentNames ().size () != rhs.getArgumentNames ().size ())
4139
4156
return limit;
4140
4157
4141
- // Score the base name match.
4142
- unsigned score = scoreIdentifiers (lhs.getBaseName (), rhs.getBaseName (),
4143
- limit, false );
4158
+ // Score the base name match. If there is a first argument for a
4159
+ // function, include its text along with the base name's text.
4160
+ unsigned score;
4161
+ if (lhs.getArgumentNames ().empty () || !isFunc) {
4162
+ score = scoreIdentifiers (lhs.getBaseName (), rhs.getBaseName (), limit);
4163
+ } else {
4164
+ llvm::SmallString<16 > lhsScratch;
4165
+ StringRef lhsFirstName =
4166
+ combineBaseNameAndFirstArgument (lhs.getBaseName (),
4167
+ lhs.getArgumentNames ()[0 ],
4168
+ lhsScratch);
4169
+
4170
+ llvm::SmallString<16 > rhsScratch;
4171
+ StringRef rhsFirstName =
4172
+ combineBaseNameAndFirstArgument (rhs.getBaseName (),
4173
+ rhs.getArgumentNames ()[0 ],
4174
+ rhsScratch);
4175
+
4176
+ score = lhsFirstName.edit_distance (rhsFirstName.str (), true , limit);
4177
+ }
4144
4178
if (score >= limit) return limit;
4145
4179
4146
4180
// Compute the edit distance between matching argument names.
4147
- for (unsigned i = 0 ; i != lhs.getArgumentNames ().size (); ++i) {
4181
+ for (unsigned i = isFunc ? 1 : 0 ; i < lhs.getArgumentNames ().size (); ++i) {
4148
4182
score += scoreIdentifiers (lhs.getArgumentNames ()[i],
4149
4183
rhs.getArgumentNames ()[i],
4150
- limit - score,
4151
- isFunc && i == 0 );
4184
+ limit - score);
4152
4185
if (score >= limit) return limit;
4153
4186
}
4154
4187
@@ -4270,15 +4303,14 @@ static bool shouldWarnAboutPotentialWitness(ValueDecl *req,
4270
4303
if (!attr->isImplicit ()) return false ;
4271
4304
}
4272
4305
4273
- // If the score is relatively high, don't warn: this is probably unrelated.
4274
- // The heuristic we use here is to ignore outright omissions and determine
4275
- // whether there was more than one typo for every two characters. If so,
4276
- // consider it "different" .
4306
+ // If the score is relatively high, don't warn: this is probably
4307
+ // unrelated. Allow about one typo for every two properly-typed
4308
+ // characters, which prevents completely-wacky suggestions in many
4309
+ // cases .
4277
4310
unsigned reqNameLen = getNameLength (req->getFullName ());
4278
4311
unsigned witnessNameLen = getNameLength (witness->getFullName ());
4279
- unsigned lengthDiff =
4280
- std::max (reqNameLen, witnessNameLen) - std::min (reqNameLen, witnessNameLen);
4281
- if ((score - lengthDiff) > (witnessNameLen + 1 ) / 3 ) return false ;
4312
+ if (score > (std::min (reqNameLen, witnessNameLen) + 1 ) / 3 )
4313
+ return false ;
4282
4314
4283
4315
return true ;
4284
4316
}
@@ -4380,8 +4412,7 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc,
4380
4412
/* sorted=*/ true );
4381
4413
// Catalog all of members of this declaration context that satisfy
4382
4414
// requirements of conformances in this context.
4383
- llvm::MapVector<DeclName, llvm::TinyPtrVector<ValueDecl *>>
4384
- unsatisfiedReqs;
4415
+ SmallVector<ValueDecl *, 16 > unsatisfiedReqs;
4385
4416
4386
4417
bool anyInvalid = false ;
4387
4418
for (auto conformance : conformances) {
@@ -4405,7 +4436,7 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc,
4405
4436
// If the requirement is unsatisfied, we might want to warn
4406
4437
// about near misses; record it.
4407
4438
if (isUnsatisfiedReq (normal, req)) {
4408
- unsatisfiedReqs[req-> getBaseName ()] .push_back (req);
4439
+ unsatisfiedReqs.push_back (req);
4409
4440
continue ;
4410
4441
}
4411
4442
}
@@ -4462,16 +4493,11 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc,
4462
4493
if (knownWitnesses.count (value) > 0 ) continue ;
4463
4494
if (!value->getFullName ()) continue ;
4464
4495
4465
- // Consider any unsatisfied requirements with the same base
4466
- // name.
4467
- auto reqs = unsatisfiedReqs.find (value->getBaseName ());
4468
- if (reqs == unsatisfiedReqs.end ()) continue ;
4469
-
4470
4496
// Find the unsatisfied requirements with the nearest-matching
4471
4497
// names.
4472
4498
SmallVector<ValueDecl *, 4 > bestOptionalReqs;
4473
4499
unsigned bestScore = UINT_MAX;
4474
- for (auto req : reqs-> second ) {
4500
+ for (auto req : unsatisfiedReqs ) {
4475
4501
// Score this particular optional requirement.
4476
4502
auto score = scorePotentiallyMatchingNames (value->getFullName (),
4477
4503
req->getFullName (),
@@ -4511,37 +4537,31 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc,
4511
4537
4512
4538
// Remove this optional requirement from the list. We don't want to
4513
4539
// complain about it twice.
4514
- if (reqs->second .size () == 1 ) {
4515
- unsatisfiedReqs.erase (reqs);
4516
- } else {
4517
- reqs->second .erase (std::find (reqs->second .begin (),
4518
- reqs->second .end (),
4519
- req));
4520
- }
4540
+ unsatisfiedReqs.erase (std::find (unsatisfiedReqs.begin (),
4541
+ unsatisfiedReqs.end (),
4542
+ req));
4521
4543
}
4522
4544
}
4523
4545
4524
4546
// For any unsatified optional @objc requirements that remain
4525
4547
// unsatisfied, note them in the AST for @objc selector collision
4526
4548
// checking.
4527
- for (const auto &unsatisfied : unsatisfiedReqs) {
4528
- for (auto req : unsatisfied.second ) {
4529
- // Skip non-@objc requirements.
4530
- if (!req->isObjC ()) continue ;
4549
+ for (auto req : unsatisfiedReqs) {
4550
+ // Skip non-@objc requirements.
4551
+ if (!req->isObjC ()) continue ;
4531
4552
4532
- // Skip unavailable requirements.
4533
- if (req->getAttrs ().isUnavailable (Context)) continue ;
4553
+ // Skip unavailable requirements.
4554
+ if (req->getAttrs ().isUnavailable (Context)) continue ;
4534
4555
4535
- // Record this requirement.
4536
- if (auto funcReq = dyn_cast<AbstractFunctionDecl>(req)) {
4537
- Context.recordObjCUnsatisfiedOptReq (dc, funcReq);
4538
- } else {
4539
- auto storageReq = cast<AbstractStorageDecl>(req);
4540
- if (auto getter = storageReq->getGetter ())
4541
- Context.recordObjCUnsatisfiedOptReq (dc, getter);
4542
- if (auto setter = storageReq->getSetter ())
4543
- Context.recordObjCUnsatisfiedOptReq (dc, setter);
4544
- }
4556
+ // Record this requirement.
4557
+ if (auto funcReq = dyn_cast<AbstractFunctionDecl>(req)) {
4558
+ Context.recordObjCUnsatisfiedOptReq (dc, funcReq);
4559
+ } else {
4560
+ auto storageReq = cast<AbstractStorageDecl>(req);
4561
+ if (auto getter = storageReq->getGetter ())
4562
+ Context.recordObjCUnsatisfiedOptReq (dc, getter);
4563
+ if (auto setter = storageReq->getSetter ())
4564
+ Context.recordObjCUnsatisfiedOptReq (dc, setter);
4545
4565
}
4546
4566
}
4547
4567
}
0 commit comments