@@ -1873,7 +1873,13 @@ class FailureDiagnosis :public ASTVisitor<FailureDiagnosis, /*exprresult*/bool>{
1873
1873
// / Produce a diagnostic for a general conversion failure (irrespective of the
1874
1874
// / exact expression kind).
1875
1875
bool diagnoseGeneralConversionFailure (Constraint *constraint);
1876
-
1876
+
1877
+ // / Produce a diagnostic for binary comparisons of the nil literal
1878
+ // / to other values.
1879
+ bool diagnoseNilLiteralComparison (Expr *lhsExpr, Expr *rhsExpr,
1880
+ CalleeCandidateInfo &calleeInfo,
1881
+ SourceLoc applyLoc);
1882
+
1877
1883
bool visitExpr (Expr *E);
1878
1884
bool visitIdentityExpr (IdentityExpr *E);
1879
1885
bool visitTupleExpr (TupleExpr *E);
@@ -4360,6 +4366,60 @@ static bool isNameOfStandardComparisonOperator(StringRef opName) {
4360
4366
opName == " <=" || opName == " >=" ;
4361
4367
}
4362
4368
4369
+ bool FailureDiagnosis::diagnoseNilLiteralComparison (
4370
+ Expr *lhsExpr, Expr *rhsExpr, CalleeCandidateInfo &calleeInfo,
4371
+ SourceLoc applyLoc) {
4372
+
4373
+ auto overloadName = calleeInfo.declName ;
4374
+
4375
+ // Only diagnose for comparison operators.
4376
+ if (!isNameOfStandardComparisonOperator (overloadName))
4377
+ return false ;
4378
+
4379
+ Expr *otherExpr = lhsExpr;
4380
+ Expr *nilExpr = rhsExpr;
4381
+
4382
+ // Swap if we picked the wrong side as the nil literal.
4383
+ if (!isa<NilLiteralExpr>(nilExpr->getValueProvidingExpr ()))
4384
+ std::swap (otherExpr, nilExpr);
4385
+
4386
+ // Bail if neither side is a nil literal.
4387
+ if (!isa<NilLiteralExpr>(nilExpr->getValueProvidingExpr ()))
4388
+ return false ;
4389
+
4390
+ // Bail if both sides are a nil literal.
4391
+ if (isa<NilLiteralExpr>(otherExpr->getValueProvidingExpr ()))
4392
+ return false ;
4393
+
4394
+ auto otherType = otherExpr->getType ()->getRValueType ();
4395
+
4396
+ // Bail if we were unable to determine the other type.
4397
+ if (isUnresolvedOrTypeVarType (otherType))
4398
+ return false ;
4399
+
4400
+ // Regardless of whether the type has reference or value semantics,
4401
+ // comparison with nil is illegal, albeit for different reasons spelled
4402
+ // out by the diagnosis.
4403
+ if (otherType->getAnyOptionalObjectType () &&
4404
+ (overloadName == " !==" || overloadName == " ===" )) {
4405
+ auto revisedName = overloadName;
4406
+ revisedName.pop_back ();
4407
+
4408
+ // If we made it here, then we're trying to perform a comparison with
4409
+ // reference semantics rather than value semantics. The fixit will
4410
+ // lop off the extra '=' in the operator.
4411
+ diagnose (applyLoc,
4412
+ diag::value_type_comparison_with_nil_illegal_did_you_mean,
4413
+ otherType)
4414
+ .fixItReplace (applyLoc, revisedName);
4415
+ } else {
4416
+ diagnose (applyLoc, diag::value_type_comparison_with_nil_illegal, otherType)
4417
+ .highlight (otherExpr->getSourceRange ());
4418
+ }
4419
+
4420
+ return true ;
4421
+ }
4422
+
4363
4423
bool FailureDiagnosis::visitApplyExpr (ApplyExpr *callExpr) {
4364
4424
// Type check the function subexpression to resolve a type for it if possible.
4365
4425
auto fnExpr = typeCheckChildIndependently (callExpr->getFn ());
@@ -4527,34 +4587,11 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
4527
4587
auto lhsExpr = argTuple->getElement (0 ), rhsExpr = argTuple->getElement (1 );
4528
4588
auto lhsType = lhsExpr->getType ()->getRValueType ();
4529
4589
auto rhsType = rhsExpr->getType ()->getRValueType ();
4530
-
4531
- // If this is a comparison against nil, then we should produce a specific
4532
- // diagnostic.
4533
- if (isa<NilLiteralExpr>(rhsExpr->getValueProvidingExpr ()) &&
4534
- !isUnresolvedOrTypeVarType (lhsType)) {
4535
- if (isNameOfStandardComparisonOperator (overloadName)) {
4536
- // Regardless of whether the type has reference or value semantics,
4537
- // comparison with nil is illegal, albeit for different reasons spelled
4538
- // out by the diagnosis.
4539
- if (lhsType->getAnyOptionalObjectType () &&
4540
- (overloadName == " !==" || overloadName == " ===" )) {
4541
- auto revisedName = overloadName;
4542
- revisedName.pop_back ();
4543
- // If we made it here, then we're trying to perform a comparison with
4544
- // reference semantics rather than value semantics. The fixit will
4545
- // lop off the extra '=' in the operator.
4546
- diagnose (callExpr->getLoc (),
4547
- diag::value_type_comparison_with_nil_illegal_did_you_mean,
4548
- lhsType)
4549
- .fixItReplace (callExpr->getLoc (), revisedName);
4550
- } else {
4551
- diagnose (callExpr->getLoc (),
4552
- diag::value_type_comparison_with_nil_illegal, lhsType)
4553
- .highlight (lhsExpr->getSourceRange ());
4554
- }
4555
- return true ;
4556
- }
4557
- }
4590
+
4591
+ // Diagnose any comparisons with the nil literal.
4592
+ if (diagnoseNilLiteralComparison (lhsExpr, rhsExpr, calleeInfo,
4593
+ callExpr->getLoc ()))
4594
+ return true ;
4558
4595
4559
4596
if (callExpr->isImplicit () && overloadName == " ~=" ) {
4560
4597
// This binop was synthesized when typechecking an expression pattern.
0 commit comments