Skip to content

Commit 6d178e8

Browse files
authored
Merge pull request #26710 from LucianoPAlmeida/SR-11295-warning-unecessary-casts
[Diagnostics][Qol] SR-11295 Emit diagnostics for same type coercion.
2 parents 4638fe1 + d3abe34 commit 6d178e8

38 files changed

+295
-115
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -949,18 +949,22 @@ ERROR(super_initializer_not_in_initializer,none,
949949
WARNING(isa_is_always_true,none, "'%0' test is always true",
950950
(StringRef))
951951
WARNING(isa_is_foreign_check,none,
952-
"'is' test is always true because %0 is a Core Foundation type",
953-
(Type))
952+
"'is' test is always true because %0 is a Core Foundation type",
953+
(Type))
954954
WARNING(conditional_downcast_coercion,none,
955-
"conditional cast from %0 to %1 always succeeds",
956-
(Type, Type))
957-
955+
"conditional cast from %0 to %1 always succeeds",
956+
(Type, Type))
957+
WARNING(unnecessary_same_type_coercion,none,
958+
"redundant cast to %0 has no effect",
959+
(Type))
960+
WARNING(unnecessary_same_typealias_type_coercion,none,
961+
"redundant cast from %0 to %1 has no effect",
962+
(Type, Type))
958963
WARNING(forced_downcast_noop,none,
959964
"forced cast of %0 to same type has no effect", (Type))
960-
961965
WARNING(forced_downcast_coercion,none,
962-
"forced cast from %0 to %1 always succeeds; did you mean to use 'as'?",
963-
(Type, Type))
966+
"forced cast from %0 to %1 always succeeds; did you mean to use 'as'?",
967+
(Type, Type))
964968

965969
// Note: the Boolean at the end indicates whether bridging is required after
966970
// the cast.

lib/Sema/CSDiagnostics.cpp

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4427,9 +4427,7 @@ bool InaccessibleMemberFailure::diagnoseAsError() {
44274427
auto &cs = getConstraintSystem();
44284428
auto *locator =
44294429
cs.getConstraintLocator(baseExpr, ConstraintLocator::Member);
4430-
if (llvm::any_of(cs.getFixes(), [&](const ConstraintFix *fix) {
4431-
return fix->getLocator() == locator;
4432-
}))
4430+
if (cs.hasFixFor(locator))
44334431
return false;
44344432
}
44354433

@@ -5058,6 +5056,36 @@ bool ThrowingFunctionConversionFailure::diagnoseAsError() {
50585056
return true;
50595057
}
50605058

5059+
bool UnnecessaryCoercionFailure::diagnoseAsError() {
5060+
auto expr = cast<CoerceExpr>(getAnchor());
5061+
auto sourceRange =
5062+
SourceRange(expr->getLoc(), expr->getCastTypeLoc().getSourceRange().End);
5063+
5064+
if (isa<TypeAliasType>(getFromType().getPointer()) &&
5065+
isa<TypeAliasType>(getToType().getPointer())) {
5066+
auto fromTypeAlias = cast<TypeAliasType>(getFromType().getPointer());
5067+
auto toTypeAlias = cast<TypeAliasType>(getToType().getPointer());
5068+
// If the typealias are different, we need a warning
5069+
// mentioning both types.
5070+
if (fromTypeAlias->getDecl() != toTypeAlias->getDecl()) {
5071+
emitDiagnostic(expr->getLoc(),
5072+
diag::unnecessary_same_typealias_type_coercion,
5073+
getFromType(), getToType())
5074+
5075+
.fixItRemove(sourceRange);
5076+
} else {
5077+
emitDiagnostic(expr->getLoc(), diag::unnecessary_same_type_coercion,
5078+
getToType())
5079+
.fixItRemove(sourceRange);
5080+
}
5081+
} else {
5082+
emitDiagnostic(expr->getLoc(), diag::unnecessary_same_type_coercion,
5083+
getToType())
5084+
.fixItRemove(sourceRange);
5085+
}
5086+
return true;
5087+
}
5088+
50615089
bool InOutConversionFailure::diagnoseAsError() {
50625090
auto *anchor = getAnchor();
50635091
auto *locator = getLocator();

lib/Sema/CSDiagnostics.h

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -709,8 +709,8 @@ class ContextualFailure : public FailureDiagnostic {
709709
/// If we're trying to convert something to `nil`.
710710
bool diagnoseConversionToNil() const;
711711

712-
// If we're trying to convert something of type "() -> T" to T,
713-
// then we probably meant to call the value.
712+
/// If we're trying to convert something of type "() -> T" to T,
713+
/// then we probably meant to call the value.
714714
bool diagnoseMissingFunctionCall() const;
715715

716716
/// Produce a specialized diagnostic if this is an invalid conversion to Bool.
@@ -1462,11 +1462,11 @@ class MutatingMemberRefOnImmutableBase final : public FailureDiagnostic {
14621462
bool diagnoseAsError() override;
14631463
};
14641464

1465-
// Diagnose an attempt to use AnyObject as the root type of a KeyPath
1466-
//
1467-
// ```swift
1468-
// let keyPath = \AnyObject.bar
1469-
// ```
1465+
/// Diagnose an attempt to use AnyObject as the root type of a KeyPath
1466+
///
1467+
/// ```swift
1468+
/// let keyPath = \AnyObject.bar
1469+
/// ```
14701470
class AnyObjectKeyPathRootFailure final : public FailureDiagnostic {
14711471

14721472
public:
@@ -1776,6 +1776,27 @@ class ExpandArrayIntoVarargsFailure final : public ContextualFailure {
17761776
void tryDropArrayBracketsFixIt(Expr *anchor) const;
17771777
};
17781778

1779+
/// Diagnose a situation where there is an explicit type coercion
1780+
/// to the same type e.g.:
1781+
///
1782+
/// ```swift
1783+
/// Double(1) as Double // redundant cast to 'Double' has no effect
1784+
/// 1 as Double as Double // redundant cast to 'Double' has no effect
1785+
/// let string = "String"
1786+
/// let s = string as String // redundant cast to 'String' has no effect
1787+
/// ```
1788+
class UnnecessaryCoercionFailure final
1789+
: public ContextualFailure {
1790+
1791+
public:
1792+
UnnecessaryCoercionFailure(Expr *root, ConstraintSystem &cs,
1793+
Type fromType, Type toType,
1794+
ConstraintLocator *locator)
1795+
: ContextualFailure(root, cs, fromType, toType, locator) {}
1796+
1797+
bool diagnoseAsError() override;
1798+
};
1799+
17791800
/// Diagnose a situation there is a mismatch between argument and parameter
17801801
/// types e.g.:
17811802
///

lib/Sema/CSFix.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,38 @@ IgnoreContextualType *IgnoreContextualType::create(ConstraintSystem &cs,
857857
IgnoreContextualType(cs, resultTy, specifiedTy, locator);
858858
}
859859

860+
bool RemoveUnnecessaryCoercion::diagnose(Expr *root, bool asNote) const {
861+
auto &cs = getConstraintSystem();
862+
UnnecessaryCoercionFailure failure(root, cs, getFromType(), getToType(),
863+
getLocator());
864+
return failure.diagnose(asNote);
865+
}
866+
867+
bool RemoveUnnecessaryCoercion::attempt(ConstraintSystem &cs, Type fromType,
868+
Type toType,
869+
ConstraintLocatorBuilder locator) {
870+
auto last = locator.last();
871+
bool isExplicitCoercion =
872+
last && last->is<LocatorPathElt::ExplicitTypeCoercion>();
873+
if (!isExplicitCoercion)
874+
return false;
875+
876+
auto expr = cast<CoerceExpr>(locator.getAnchor());
877+
auto toTypeRepr = expr->getCastTypeLoc().getTypeRepr();
878+
879+
// only diagnosing for coercion where the left-side is a DeclRefExpr
880+
// or a explicit/implicit coercion e.g. Double(1) as Double
881+
if (!isa<ImplicitlyUnwrappedOptionalTypeRepr>(toTypeRepr) &&
882+
(isa<DeclRefExpr>(expr->getSubExpr()) ||
883+
isa<CoerceExpr>(expr->getSubExpr()))) {
884+
auto *fix = new (cs.getAllocator()) RemoveUnnecessaryCoercion(
885+
cs, fromType, toType, cs.getConstraintLocator(locator));
886+
887+
return cs.recordFix(fix);
888+
}
889+
return false;
890+
}
891+
860892
bool IgnoreAssignmentDestinationType::diagnose(Expr *root, bool asNote) const {
861893
auto &cs = getConstraintSystem();
862894
auto *AE = cast<AssignExpr>(getAnchor());

lib/Sema/CSFix.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,10 @@ enum class FixKind : uint8_t {
196196
/// Allow a single tuple parameter to be matched with N arguments
197197
/// by forming all of the given arguments into a single tuple.
198198
AllowTupleSplatForSingleParameter,
199+
200+
/// Remove an unnecessary coercion ('as') if the types are already equal.
201+
/// e.g. Double(1) as Double
202+
RemoveUnnecessaryCoercion,
199203

200204
/// Allow a single argument type mismatch. This is the most generic
201205
/// failure related to argument-to-parameter conversions.
@@ -1340,6 +1344,24 @@ class IgnoreContextualType : public ContextualMismatch {
13401344
ConstraintLocator *locator);
13411345
};
13421346

1347+
class RemoveUnnecessaryCoercion : public ContextualMismatch {
1348+
protected:
1349+
RemoveUnnecessaryCoercion(ConstraintSystem &cs, Type fromType, Type toType,
1350+
ConstraintLocator *locator)
1351+
: ContextualMismatch(cs, FixKind::RemoveUnnecessaryCoercion, fromType,
1352+
toType, locator, /*isWarning*/ true) {}
1353+
1354+
public:
1355+
std::string getName() const override {
1356+
return "remove unnecessary explicit type coercion";
1357+
}
1358+
1359+
bool diagnose(Expr *root, bool asNote = false) const override;
1360+
1361+
static bool attempt(ConstraintSystem &cs, Type fromType, Type toType,
1362+
ConstraintLocatorBuilder locator);
1363+
};
1364+
13431365
class IgnoreAssignmentDestinationType final : public ContextualMismatch {
13441366
IgnoreAssignmentDestinationType(ConstraintSystem &cs, Type sourceTy,
13451367
Type destTy, ConstraintLocator *locator)

lib/Sema/CSSimplify.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2587,7 +2587,8 @@ bool ConstraintSystem::repairFailures(
25872587
});
25882588
};
25892589

2590-
if (path.empty()) {
2590+
if (path.empty() ||
2591+
path.back().is<LocatorPathElt::ExplicitTypeCoercion>()) {
25912592
if (!anchor)
25922593
return false;
25932594

@@ -3218,9 +3219,16 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
32183219
// let's defer it until later proper check.
32193220
if (!(desugar1->is<DependentMemberType>() &&
32203221
desugar2->is<DependentMemberType>())) {
3221-
// If the types are obviously equivalent, we're done.
3222-
if (desugar1->isEqual(desugar2) && !isa<InOutType>(desugar2)) {
3223-
return getTypeMatchSuccess();
3222+
if (desugar1->isEqual(desugar2)) {
3223+
if (kind >= ConstraintKind::Conversion &&
3224+
!flags.contains(TMF_ApplyingFix)) {
3225+
if (RemoveUnnecessaryCoercion::attempt(*this, type1, type2,
3226+
getConstraintLocator(locator))) {
3227+
return getTypeMatchFailure(locator);
3228+
}
3229+
}
3230+
if (!isa<InOutType>(desugar2))
3231+
return getTypeMatchSuccess();
32243232
}
32253233
}
32263234

@@ -8029,6 +8037,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
80298037
case FixKind::GenericArgumentsMismatch:
80308038
case FixKind::AllowMutatingMemberOnRValueBase:
80318039
case FixKind::AllowTupleSplatForSingleParameter:
8040+
case FixKind::RemoveUnnecessaryCoercion:
80328041
llvm_unreachable("handled elsewhere");
80338042
}
80348043

@@ -8335,11 +8344,20 @@ void ConstraintSystem::addExplicitConversionConstraint(
83358344
SmallVector<Constraint *, 3> constraints;
83368345

83378346
auto locatorPtr = getConstraintLocator(locator);
8347+
ConstraintLocator *coerceLocator = locatorPtr;
8348+
8349+
if (allowFixes && shouldAttemptFixes()) {
8350+
auto *anchor = locator.getAnchor();
8351+
if (isa<CoerceExpr>(anchor) && !anchor->isImplicit()) {
8352+
coerceLocator =
8353+
getConstraintLocator(anchor, LocatorPathElt::ExplicitTypeCoercion());
8354+
}
8355+
}
83388356

83398357
// Coercion (the common case).
83408358
Constraint *coerceConstraint =
83418359
Constraint::create(*this, ConstraintKind::Conversion,
8342-
fromType, toType, locatorPtr);
8360+
fromType, toType, coerceLocator);
83438361
coerceConstraint->setFavored();
83448362
constraints.push_back(coerceConstraint);
83458363

lib/Sema/ConstraintLocator.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ void ConstraintLocator::Profile(llvm::FoldingSetNodeID &id, Expr *anchor,
8686
case KeyPathRoot:
8787
case KeyPathValue:
8888
case KeyPathComponentResult:
89+
case ExplicitTypeCoercion:
8990
case Condition:
9091
auto numValues = numNumericValuesInPathElement(elt.getKind());
9192
for (unsigned i = 0; i < numValues; ++i)
@@ -133,6 +134,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const {
133134
case ConstraintLocator::KeyPathRoot:
134135
case ConstraintLocator::KeyPathValue:
135136
case ConstraintLocator::KeyPathComponentResult:
137+
case ConstraintLocator::ExplicitTypeCoercion:
136138
case ConstraintLocator::Condition:
137139
return 0;
138140

@@ -458,7 +460,11 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) {
458460
case KeyPathComponentResult:
459461
out << "key path component result";
460462
break;
461-
463+
464+
case ExplicitTypeCoercion:
465+
out << "type coercion";
466+
break;
467+
462468
case Condition:
463469
out << "condition expression";
464470
break;

lib/Sema/ConstraintLocator.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ class ConstraintLocator : public llvm::FoldingSetNode {
8989
case KeyPathRoot:
9090
case KeyPathValue:
9191
case KeyPathComponentResult:
92+
case ExplicitTypeCoercion:
9293
case Condition:
9394
return 0;
9495

lib/Sema/ConstraintLocatorPathElts.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,9 @@ SIMPLE_LOCATOR_PATH_ELT(UnresolvedMember)
164164
/// The candidate witness during protocol conformance checking.
165165
CUSTOM_LOCATOR_PATH_ELT(Witness)
166166

167+
/// An explicit type coercion.
168+
SIMPLE_LOCATOR_PATH_ELT(ExplicitTypeCoercion)
169+
167170
/// The condition associated with 'if' expression or ternary operator.
168171
SIMPLE_LOCATOR_PATH_ELT(Condition)
169172

lib/Sema/ConstraintSystem.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3000,6 +3000,11 @@ void constraints::simplifyLocator(Expr *&anchor,
30003000
path = path.slice(1);
30013001
continue;
30023002
}
3003+
3004+
case ConstraintLocator::ExplicitTypeCoercion: {
3005+
path = path.slice(1);
3006+
continue;
3007+
}
30033008

30043009
default:
30053010
// FIXME: Lots of other cases to handle.

0 commit comments

Comments
 (0)