Skip to content

Commit 099430d

Browse files
committed
[Parse][GSB] Add fix-it to remove redundant ': Any' constraints
Note that this doesn't handle constraints that are parsed as inheritance clauses, such as <T : Any> as we don't currently have a custom data structure by which to represent it in the GSB.
1 parent 94e4e38 commit 099430d

File tree

7 files changed

+170
-62
lines changed

7 files changed

+170
-62
lines changed

include/swift/AST/Decl.h

Lines changed: 57 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,12 @@ enum class RequirementReprKind : unsigned {
10121012
/// \c GenericParamList assumes these are POD-like.
10131013
class RequirementRepr {
10141014
SourceLoc SeparatorLoc;
1015+
1016+
/// This range includes everything that needs to be removed to keep the
1017+
/// program syntactically valid (e.g. where and commas), unlike just from
1018+
/// the start of \c FirstType to the end of \c SecondType.
1019+
SourceRange RemovalRange;
1020+
10151021
RequirementReprKind Kind : 2;
10161022
bool Invalid : 1;
10171023
TypeLoc FirstType;
@@ -1027,15 +1033,17 @@ class RequirementRepr {
10271033
/// for the generated interface.
10281034
StringRef AsWrittenString;
10291035

1030-
RequirementRepr(SourceLoc SeparatorLoc, RequirementReprKind Kind,
1031-
TypeLoc FirstType, TypeLoc SecondType)
1032-
: SeparatorLoc(SeparatorLoc), Kind(Kind), Invalid(false),
1033-
FirstType(FirstType), SecondType(SecondType) { }
1036+
RequirementRepr(SourceLoc SeparatorLoc, SourceRange RemovalRange,
1037+
RequirementReprKind Kind, TypeLoc FirstType,
1038+
TypeLoc SecondType)
1039+
: SeparatorLoc(SeparatorLoc), RemovalRange(RemovalRange), Kind(Kind),
1040+
Invalid(false), FirstType(FirstType), SecondType(SecondType) {}
10341041

1035-
RequirementRepr(SourceLoc SeparatorLoc, RequirementReprKind Kind,
1036-
TypeLoc FirstType, LayoutConstraintLoc SecondLayout)
1037-
: SeparatorLoc(SeparatorLoc), Kind(Kind), Invalid(false),
1038-
FirstType(FirstType), SecondLayout(SecondLayout) { }
1042+
RequirementRepr(SourceLoc SeparatorLoc, SourceRange RemovalRange,
1043+
RequirementReprKind Kind, TypeLoc FirstType,
1044+
LayoutConstraintLoc SecondLayout)
1045+
: SeparatorLoc(SeparatorLoc), RemovalRange(RemovalRange), Kind(Kind),
1046+
Invalid(false), FirstType(FirstType), SecondLayout(SecondLayout) {}
10391047

10401048
void printImpl(ASTPrinter &OS, bool AsWritten) const;
10411049

@@ -1048,10 +1056,15 @@ class RequirementRepr {
10481056
/// this requirement was implied.
10491057
/// \param Constraint The protocol or protocol composition to which the
10501058
/// subject must conform, or superclass from which the subject must inherit.
1051-
static RequirementRepr getTypeConstraint(TypeLoc Subject,
1052-
SourceLoc ColonLoc,
1053-
TypeLoc Constraint) {
1054-
return { ColonLoc, RequirementReprKind::TypeConstraint, Subject, Constraint };
1059+
/// \param RemovalRange A source range suitable for removing the requirement
1060+
/// from the associated where clause, including everything that needs to be
1061+
/// removed to keep the program syntactically valid (e.g. the 'where' keyword
1062+
/// and commas).
1063+
static RequirementRepr getTypeConstraint(TypeLoc Subject, SourceLoc ColonLoc,
1064+
TypeLoc Constraint,
1065+
SourceRange RemovalRange) {
1066+
return {ColonLoc, RemovalRange, RequirementReprKind::TypeConstraint,
1067+
Subject, Constraint};
10551068
}
10561069

10571070
/// \brief Construct a new same-type requirement.
@@ -1060,25 +1073,34 @@ class RequirementRepr {
10601073
/// \param EqualLoc The location of the '==' in the same-type constraint, or
10611074
/// an invalid location if this requirement was implied.
10621075
/// \param SecondType The second type.
1063-
static RequirementRepr getSameType(TypeLoc FirstType,
1064-
SourceLoc EqualLoc,
1065-
TypeLoc SecondType) {
1066-
return { EqualLoc, RequirementReprKind::SameType, FirstType, SecondType };
1076+
/// \param RemovalRange A source range suitable for removing the requirement
1077+
/// from the associated where clause, including everything that needs to be
1078+
/// removed to keep the program syntactically valid (e.g. the 'where' keyword
1079+
/// and commas).
1080+
static RequirementRepr getSameType(TypeLoc FirstType, SourceLoc EqualLoc,
1081+
TypeLoc SecondType,
1082+
SourceRange RemovalRange) {
1083+
return {EqualLoc, RemovalRange, RequirementReprKind::SameType, FirstType,
1084+
SecondType};
10671085
}
10681086

10691087
/// \brief Construct a new layout-constraint requirement.
10701088
///
1071-
/// \param Subject The type that must conform to the given layout
1072-
/// requirement.
1089+
/// \param Subject The type that must conform to the given layout requirement.
10731090
/// \param ColonLoc The location of the ':', or an invalid location if
10741091
/// this requirement was implied.
10751092
/// \param Layout The layout requirement to which the
10761093
/// subject must conform.
1094+
/// \param RemovalRange A source range suitable for removing the requirement
1095+
/// from the associated where clause, including everything that needs to be
1096+
/// removed to keep the program syntactically valid (e.g. the 'where' keyword
1097+
/// and commas).
10771098
static RequirementRepr getLayoutConstraint(TypeLoc Subject,
10781099
SourceLoc ColonLoc,
1079-
LayoutConstraintLoc Layout) {
1080-
return {ColonLoc, RequirementReprKind::LayoutConstraint, Subject,
1081-
Layout};
1100+
LayoutConstraintLoc Layout,
1101+
SourceRange RemovalRange) {
1102+
return {ColonLoc, RemovalRange, RequirementReprKind::LayoutConstraint,
1103+
Subject, Layout};
10821104
}
10831105

10841106
/// \brief Determine the kind of requirement
@@ -1161,6 +1183,20 @@ class RequirementRepr {
11611183
return SeparatorLoc;
11621184
}
11631185

1186+
/// Retrieve the source range suitable for removing the requirement. Unlike
1187+
/// \c getSourceRange(), this range includes everything that needs to be
1188+
/// removed to keep the program syntactically valid (e.g. the 'where' keyword
1189+
/// and commas).
1190+
SourceRange getRemovalRange() const { return RemovalRange; }
1191+
1192+
/// Set the source range suitable for removing the requirement. Unlike
1193+
/// \c getSourceRange(), this range includes everything that needs to be
1194+
/// removed to keep the program syntactically valid (e.g. the 'where' keyword
1195+
/// and commas).
1196+
void setRemovalRange(SourceRange removalRange) {
1197+
RemovalRange = removalRange;
1198+
}
1199+
11641200
/// \brief Retrieve the first type of a same-type requirement.
11651201
Type getFirstType() const {
11661202
assert(getKind() == RequirementReprKind::SameType);

include/swift/AST/GenericSignatureBuilder.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,6 +1282,12 @@ class GenericSignatureBuilder::RequirementSource final
12821282
/// Retrieve the source range for the requirement, if any.
12831283
SourceRange getSourceRange() const;
12841284

1285+
/// Retrieve the source range suitable for removing the requirement, if any.
1286+
/// Unlike \c getSourceRange(), this range includes everything that needs to
1287+
/// be removed to keep the program syntactically valid (e.g. the 'where'
1288+
/// keyword and commas).
1289+
SourceRange getRemovalRange() const;
1290+
12851291
/// Compare two requirement sources to determine which has the more
12861292
/// optimal path.
12871293
///
@@ -1458,6 +1464,12 @@ class GenericSignatureBuilder::FloatingRequirementSource {
14581464
/// Retrieve the source range for this requirement, if any.
14591465
SourceRange getSourceRange() const;
14601466

1467+
/// Retrieve the source range suitable for removing the requirement, if any.
1468+
/// Unlike \c getSourceRange(), this range includes everything that needs to
1469+
/// be removed to keep the program syntactically valid (e.g. the 'where'
1470+
/// keyword and commas).
1471+
SourceRange getRemovalRange() const;
1472+
14611473
/// Whether this is an explicitly-stated requirement.
14621474
bool isExplicit() const;
14631475

lib/AST/Decl.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,8 @@ GenericParamList::clone(DeclContext *dc) const {
614614
reqt = RequirementRepr::getTypeConstraint(
615615
first.clone(ctx),
616616
reqt.getColonLoc(),
617-
second.clone(ctx));
617+
second.clone(ctx),
618+
reqt.getRemovalRange());
618619
break;
619620
}
620621
case RequirementReprKind::SameType: {
@@ -623,7 +624,8 @@ GenericParamList::clone(DeclContext *dc) const {
623624
reqt = RequirementRepr::getSameType(
624625
first.clone(ctx),
625626
reqt.getEqualLoc(),
626-
second.clone(ctx));
627+
second.clone(ctx),
628+
reqt.getRemovalRange());
627629
break;
628630
}
629631
case RequirementReprKind::LayoutConstraint: {
@@ -632,7 +634,8 @@ GenericParamList::clone(DeclContext *dc) const {
632634
reqt = RequirementRepr::getLayoutConstraint(
633635
first.clone(ctx),
634636
reqt.getColonLoc(),
635-
layout);
637+
layout,
638+
reqt.getRemovalRange());
636639
break;
637640
}
638641
}

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1479,6 +1479,14 @@ SourceRange RequirementSource::getSourceRange() const {
14791479
return SourceRange();
14801480
}
14811481

1482+
SourceRange RequirementSource::getRemovalRange() const {
1483+
if (auto requirementRepr = getRequirementRepr())
1484+
return requirementRepr->getRemovalRange();
1485+
1486+
// FIX-ME: Handle removal ranges for inherited types.
1487+
return SourceRange();
1488+
}
1489+
14821490
/// Compute the path length of a requirement source, counting only the number
14831491
/// of \c ProtocolRequirement elements.
14841492
static unsigned sourcePathLength(const RequirementSource *source) {
@@ -1726,6 +1734,23 @@ SourceRange FloatingRequirementSource::getSourceRange() const {
17261734
return SourceRange();
17271735
}
17281736

1737+
SourceRange FloatingRequirementSource::getRemovalRange() const {
1738+
// If this is a source for a protocol requirement, get the removal range
1739+
// for that requirement.
1740+
if (kind == Kind::AbstractProtocol)
1741+
if (auto *written = protocolReq.written.dyn_cast<const RequirementRepr *>())
1742+
return written->getRemovalRange();
1743+
1744+
if (auto *requirement = storage.dyn_cast<const RequirementRepr *>())
1745+
return requirement->getRemovalRange();
1746+
1747+
if (auto *source = storage.dyn_cast<const RequirementSource *>())
1748+
return source->getRemovalRange();
1749+
1750+
// FIX-ME: Handle removal ranges for inherited types.
1751+
return SourceRange();
1752+
}
1753+
17291754
bool FloatingRequirementSource::isExplicit() const {
17301755
switch (kind) {
17311756
case Explicit:
@@ -4840,7 +4865,8 @@ ConstraintResult GenericSignatureBuilder::addTypeRequirement(
48404865

48414866
Diags.diagnose(diagLoc, diag::redundant_conformance_constraint,
48424867
subjectType, constraintType)
4843-
.highlight(source.getSourceRange());
4868+
.highlight(source.getSourceRange())
4869+
.fixItRemove(source.getRemovalRange());
48444870
Diags.diagnose(diagLoc, diag::all_types_implicitly_conform_to,
48454871
constraintType);
48464872
}

lib/Parse/ParseGeneric.cpp

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@
1414
//
1515
//===----------------------------------------------------------------------===//
1616

17-
#include "swift/Parse/Parser.h"
1817
#include "swift/AST/DiagnosticsParse.h"
18+
#include "swift/Basic/Defer.h"
1919
#include "swift/Parse/CodeCompletionCallbacks.h"
20-
#include "swift/Parse/SyntaxParsingContext.h"
2120
#include "swift/Parse/Lexer.h"
21+
#include "swift/Parse/Parser.h"
22+
#include "swift/Parse/SyntaxParsingContext.h"
2223
#include "swift/Syntax/SyntaxBuilders.h"
2324
#include "swift/Syntax/SyntaxNodes.h"
2425
using namespace swift;
@@ -273,21 +274,25 @@ ParserStatus Parser::parseGenericWhereClause(
273274
SyntaxParsingContext ReqListContext(SyntaxContext,
274275
SyntaxKind::GenericRequirementList);
275276
bool HasNextReq;
277+
SourceLoc LastCommaLoc;
276278
do {
277279
SyntaxParsingContext ReqContext(SyntaxContext, SyntaxContextKind::Syntax);
278280
// Parse the leading type-identifier.
279-
ParserResult<TypeRepr> FirstType = parseTypeIdentifier();
281+
ParserResult<TypeRepr> FirstTypeResult = parseTypeIdentifier();
280282

281-
if (FirstType.hasCodeCompletion()) {
283+
if (FirstTypeResult.hasCodeCompletion()) {
282284
Status.setHasCodeCompletion();
283285
FirstTypeInComplete = true;
284286
}
285287

286-
if (FirstType.isNull()) {
288+
if (FirstTypeResult.isNull()) {
287289
Status.setIsParseError();
288290
break;
289291
}
290292

293+
auto FirstType = FirstTypeResult.get();
294+
auto FirstTypeLoc = FirstType->getStartLoc();
295+
291296
if (Tok.is(tok::colon)) {
292297
// A conformance-requirement.
293298
SourceLoc ColonLoc = consumeToken();
@@ -311,8 +316,8 @@ ParserStatus Parser::parseGenericWhereClause(
311316
} else {
312317
// Add the layout requirement.
313318
Requirements.push_back(RequirementRepr::getLayoutConstraint(
314-
FirstType.get(), ColonLoc,
315-
LayoutConstraintLoc(Layout, LayoutLoc)));
319+
FirstType, ColonLoc, LayoutConstraintLoc(Layout, LayoutLoc),
320+
SourceRange(FirstTypeLoc, PreviousLoc)));
316321
}
317322
} else {
318323
// Parse the protocol or composition.
@@ -327,7 +332,8 @@ ParserStatus Parser::parseGenericWhereClause(
327332

328333
// Add the requirement.
329334
Requirements.push_back(RequirementRepr::getTypeConstraint(
330-
FirstType.get(), ColonLoc, Protocol.get()));
335+
FirstType, ColonLoc, Protocol.get(),
336+
SourceRange(FirstTypeLoc, PreviousLoc)));
331337
}
332338
} else if ((Tok.isAnyOperator() && Tok.getText() == "==") ||
333339
Tok.is(tok::equal)) {
@@ -349,15 +355,37 @@ ParserStatus Parser::parseGenericWhereClause(
349355
}
350356

351357
// Add the requirement
352-
Requirements.push_back(RequirementRepr::getSameType(FirstType.get(),
353-
EqualLoc,
354-
SecondType.get()));
358+
Requirements.push_back(RequirementRepr::getSameType(
359+
FirstType, EqualLoc, SecondType.get(),
360+
SourceRange(FirstTypeLoc, PreviousLoc)));
355361
} else {
356362
diagnose(Tok, diag::expected_requirement_delim);
357363
Status.setIsParseError();
358364
break;
359365
}
360-
HasNextReq = consumeIf(tok::comma);
366+
SourceLoc NextCommaLoc;
367+
HasNextReq = consumeIf(tok::comma, NextCommaLoc);
368+
SWIFT_DEFER { LastCommaLoc = NextCommaLoc; };
369+
370+
auto &req = Requirements.back();
371+
auto removalRange = req.getRemovalRange();
372+
373+
if (!HasNextReq && LastCommaLoc.isInvalid()) {
374+
// Include the where clause in the removal range for the first and only
375+
// requirement.
376+
removalRange.Start = WhereLoc;
377+
}
378+
if (!HasNextReq && LastCommaLoc.isValid()) {
379+
// Include the previous comma in the removal range of the last
380+
// requirement.
381+
removalRange.Start = LastCommaLoc;
382+
}
383+
if (HasNextReq) {
384+
// Include the next comma in the removal range of a middle requirement.
385+
removalRange.End = NextCommaLoc;
386+
}
387+
req.setRemovalRange(removalRange);
388+
361389
// If there's a comma, keep parsing the list.
362390
} while (HasNextReq);
363391

lib/Sema/TypeCheckAttr.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1819,11 +1819,13 @@ void AttributeChecker::visitSpecializeAttr(SpecializeAttr *attr) {
18191819
if (interfaceFirstType->hasTypeParameter()) {
18201820
resolvedRequirements.push_back(RequirementRepr::getSameType(
18211821
TypeLoc(req.getFirstTypeRepr(), genericType), req.getEqualLoc(),
1822-
TypeLoc(req.getSecondTypeRepr(), concreteType)));
1822+
TypeLoc(req.getSecondTypeRepr(), concreteType),
1823+
req.getRemovalRange()));
18231824
} else {
18241825
resolvedRequirements.push_back(RequirementRepr::getSameType(
18251826
TypeLoc(req.getFirstTypeRepr(), concreteType), req.getEqualLoc(),
1826-
TypeLoc(req.getSecondTypeRepr(), genericType)));
1827+
TypeLoc(req.getSecondTypeRepr(), genericType),
1828+
req.getRemovalRange()));
18271829
}
18281830

18291831
// Convert the requirement into a form which uses canonical interface
@@ -1855,8 +1857,8 @@ void AttributeChecker::visitSpecializeAttr(SpecializeAttr *attr) {
18551857
// Re-create a requirement using the resolved interface types.
18561858
auto resolvedReq = RequirementRepr::getLayoutConstraint(
18571859
TypeLoc(req.getSubjectRepr(), interfaceSubjectType),
1858-
req.getColonLoc(),
1859-
req.getLayoutConstraintLoc());
1860+
req.getColonLoc(), req.getLayoutConstraintLoc(),
1861+
req.getRemovalRange());
18601862

18611863
// Add a resolved requirement.
18621864
resolvedRequirements.push_back(resolvedReq);
@@ -1897,7 +1899,8 @@ void AttributeChecker::visitSpecializeAttr(SpecializeAttr *attr) {
18971899
auto resolvedReq = RequirementRepr::getTypeConstraint(
18981900
TypeLoc(req.getSubjectRepr(), interfaceSubjectType),
18991901
req.getColonLoc(),
1900-
TypeLoc(req.getConstraintRepr(), interfaceLayoutConstraint));
1902+
TypeLoc(req.getConstraintRepr(), interfaceLayoutConstraint),
1903+
req.getRemovalRange());
19011904

19021905
// Add a resolved requirement.
19031906
resolvedRequirements.push_back(resolvedReq);

0 commit comments

Comments
 (0)