Skip to content

Commit ded9b78

Browse files
committed
Warn on redundant comparison to 'Optional.none', like we already do for 'nil'
1 parent 04f1cde commit ded9b78

File tree

2 files changed

+30
-17
lines changed

2 files changed

+30
-17
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4009,6 +4009,9 @@ WARNING(use_of_qq_on_non_optional_value,none,
40094009
WARNING(nonoptional_compare_to_nil,none,
40104010
"comparing non-optional value of type %0 to 'nil' always returns"
40114011
" %select{false|true}1", (Type, bool))
4012+
WARNING(nonoptional_compare_to_optional_none_case,none,
4013+
"comparing non-optional value of type %0 to 'Optional.none' always returns"
4014+
" %select{false|true}1", (Type, bool))
40124015
WARNING(optional_check_nonoptional,none,
40134016
"non-optional expression of type %0 used in a check for optionals",
40144017
(Type))

lib/Sema/MiscDiagnostics.cpp

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,25 +1282,19 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
12821282

12831283
}
12841284

1285-
/// Return true if this is a 'nil' literal. This looks
1286-
/// like this if the type is Optional<T>:
1287-
///
1288-
/// (dot_syntax_call_expr implicit type='Int?'
1289-
/// (declref_expr implicit decl=Optional.none)
1290-
/// (type_expr type=Int?))
1291-
///
1292-
/// Or like this if it is any other ExpressibleByNilLiteral type:
1293-
///
1294-
/// (nil_literal_expr)
1295-
///
1296-
bool isTypeCheckedOptionalNil(Expr *E) {
1285+
/// Returns true if this is a 'nil' literal
1286+
bool isNilLiteral(Expr *E) {
12971287
if (dyn_cast<NilLiteralExpr>(E)) return true;
1298-
1288+
return false;
1289+
}
1290+
1291+
/// Returns true if this is a expression is a reference to
1292+
/// the `Optional.none` case specifically (e.g. not `nil`).
1293+
bool isOptionalNoneCase(Expr *E) {
12991294
auto CE = dyn_cast<ApplyExpr>(E->getSemanticsProvidingExpr());
1300-
if (!CE || !CE->isImplicit())
1295+
if (!CE)
13011296
return false;
13021297

1303-
// First case -- Optional.none
13041298
if (auto DRE = dyn_cast<DeclRefExpr>(CE->getSemanticFn()))
13051299
return DRE->getDecl() == Ctx.getOptionalNoneDecl();
13061300

@@ -1345,9 +1339,12 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
13451339

13461340
if (calleeName == "==" || calleeName == "!=" ||
13471341
calleeName == "===" || calleeName == "!==") {
1342+
bool isTrue = calleeName == "!=" || calleeName == "!==";
1343+
1344+
// Diagnose a redundant comparison of a non-optional to a `nil` literal
13481345
if (((subExpr = isImplicitPromotionToOptional(lhs)) &&
1349-
isTypeCheckedOptionalNil(rhs)) ||
1350-
(isTypeCheckedOptionalNil(lhs) &&
1346+
isNilLiteral(rhs)) ||
1347+
(isNilLiteral(lhs) &&
13511348
(subExpr = isImplicitPromotionToOptional(rhs)))) {
13521349
bool isTrue = calleeName == "!=" || calleeName == "!==";
13531350

@@ -1357,6 +1354,19 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
13571354
.highlight(rhs->getSourceRange());
13581355
return;
13591356
}
1357+
1358+
// Diagnose a redundant comparison of a non-optional to the `Optional.none` case
1359+
if (((subExpr = isImplicitPromotionToOptional(lhs)) &&
1360+
isOptionalNoneCase(rhs)) ||
1361+
(isOptionalNoneCase(lhs) &&
1362+
(subExpr = isImplicitPromotionToOptional(rhs)))) {
1363+
1364+
Ctx.Diags.diagnose(DRE->getLoc(), diag::nonoptional_compare_to_optional_none_case,
1365+
subExpr->getType(), isTrue)
1366+
.highlight(lhs->getSourceRange())
1367+
.highlight(rhs->getSourceRange());
1368+
return;
1369+
}
13601370
}
13611371
}
13621372
};

0 commit comments

Comments
 (0)