Skip to content

Commit 682ab47

Browse files
committed
[QoI] diagnose operator fixity attrs together; improve messages
Previously, `infix` was not recognized as conflicting with `prefix` and `postfix`. We now offer to remove all but the first fixity attribute.
1 parent b31e416 commit 682ab47

File tree

5 files changed

+60
-28
lines changed

5 files changed

+60
-28
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,9 +1139,12 @@ ERROR(duplicate_attribute,none,
11391139
"duplicate %select{attribute|modifier}0", (bool))
11401140
NOTE(previous_attribute,none,
11411141
"%select{attribute|modifier}0 already specified here", (bool))
1142+
ERROR(mutually_exclusive_attrs,none,
1143+
"'%0' contradicts previous %select{attribute|modifier}2 '%1'", (StringRef, StringRef, bool))
1144+
1145+
ERROR(invalid_infix_on_func,none,
1146+
"'infix' modifier is not required or allowed on func declarations", ())
11421147

1143-
ERROR(cannot_combine_attribute,none,
1144-
"attribute '%0' cannot be combined with this attribute", (StringRef))
11451148
ERROR(expected_in_attribute_list,none,
11461149
"expected ']' or ',' in attribute list", ())
11471150

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -901,8 +901,6 @@ ERROR(operator_not_func,none,
901901
"operators must be declared with 'func'", ())
902902
ERROR(redefining_builtin_operator,none,
903903
"cannot declare a custom %0 '%1' operator", (StringRef, StringRef))
904-
ERROR(invalid_infix_on_func,none,
905-
"'infix' modifier is not required or allowed on func declarations", ())
906904
ERROR(attribute_requires_operator_identifier,none,
907905
"'%0' requires a function with an operator identifier", (StringRef))
908906
ERROR(attribute_requires_single_argument,none,

lib/Parse/ParseDecl.cpp

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -306,13 +306,6 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
306306
// Delay issuing the diagnostic until we parse the attribute.
307307
DiscardAttribute = true;
308308
}
309-
310-
if ((DK == DAK_Prefix || DK == DAK_Postfix) && !DiscardAttribute &&
311-
Attributes.getUnaryOperatorKind() != UnaryOperatorKind::None) {
312-
diagnose(Loc, diag::cannot_combine_attribute,
313-
DK == DAK_Prefix ? "postfix" : "prefix");
314-
DiscardAttribute = true;
315-
}
316309

317310
// If this is a SIL-only attribute, reject it.
318311
if ((DeclAttribute::getOptions(DK) & DeclAttribute::SILOnly) != 0 &&
@@ -1597,6 +1590,51 @@ static bool isStartOfOperatorDecl(const Token &Tok, const Token &Tok2) {
15971590
Tok2.isContextualKeyword("infix"));
15981591
}
15991592

1593+
/// \brief Diagnose issues with fixity attributes, if any.
1594+
static void diagnoseOperatorFixityAttributes(Parser &P,
1595+
DeclAttributes &Attrs,
1596+
const Decl *D) {
1597+
auto isFixityAttr = [](DeclAttribute *attr){
1598+
DeclAttrKind kind = attr->getKind();
1599+
return attr->isValid() && (kind == DAK_Prefix ||
1600+
kind == DAK_Infix ||
1601+
kind == DAK_Postfix);
1602+
};
1603+
1604+
SmallVector<DeclAttribute *, 3> fixityAttrs;
1605+
std::copy_if(Attrs.begin(), Attrs.end(),
1606+
std::back_inserter(fixityAttrs), isFixityAttr);
1607+
std::reverse(fixityAttrs.begin(), fixityAttrs.end());
1608+
1609+
for (auto it = fixityAttrs.begin(); it != fixityAttrs.end(); ++it) {
1610+
if (it != fixityAttrs.begin()) {
1611+
auto *attr = *it;
1612+
P.diagnose(attr->getLocation(), diag::mutually_exclusive_attrs,
1613+
attr->getAttrName(), fixityAttrs.front()->getAttrName(),
1614+
attr->isDeclModifier())
1615+
.fixItRemove(attr->getRange());
1616+
attr->setInvalid();
1617+
}
1618+
}
1619+
1620+
// Operator declarations must specify a fixity.
1621+
if (auto *OD = dyn_cast<OperatorDecl>(D)) {
1622+
if (fixityAttrs.empty()) {
1623+
P.diagnose(OD->getOperatorLoc(), diag::operator_decl_no_fixity);
1624+
}
1625+
}
1626+
// Infix operator is only allowed on operator declarations, not on func.
1627+
else if (isa<FuncDecl>(D)) {
1628+
if (auto *attr = Attrs.getAttribute<InfixAttr>()) {
1629+
P.diagnose(attr->getLocation(), diag::invalid_infix_on_func)
1630+
.fixItRemove(attr->getLocation());
1631+
attr->setInvalid();
1632+
}
1633+
} else {
1634+
llvm_unreachable("unexpected decl kind?");
1635+
}
1636+
}
1637+
16001638
static bool isKeywordPossibleDeclStart(const Token &Tok) {
16011639
switch (Tok.getKind()) {
16021640
case tok::at_sign:
@@ -4479,6 +4517,8 @@ Parser::parseDeclFunc(SourceLoc StaticLoc, StaticSpellingKind StaticSpelling,
44794517
GenericParams, BodyParams, Type(), FuncRetTy,
44804518
CurDeclContext);
44814519

4520+
diagnoseOperatorFixityAttributes(*this, Attributes, FD);
4521+
44824522
// Add the attributes here so if we need them while parsing the body
44834523
// they are available.
44844524
FD->getAttrs() = Attributes;
@@ -5512,23 +5552,20 @@ Parser::parseDeclOperatorImpl(SourceLoc OperatorLoc, Identifier Name,
55125552
(void) consumeIf(tok::r_brace);
55135553
}
55145554

5515-
55165555
OperatorDecl *res;
55175556
if (Attributes.hasAttribute<PrefixAttr>())
55185557
res = new (Context) PrefixOperatorDecl(CurDeclContext, OperatorLoc,
55195558
Name, NameLoc);
55205559
else if (Attributes.hasAttribute<PostfixAttr>())
55215560
res = new (Context) PostfixOperatorDecl(CurDeclContext, OperatorLoc,
55225561
Name, NameLoc);
5523-
else {
5524-
if (!Attributes.hasAttribute<InfixAttr>())
5525-
diagnose(OperatorLoc, diag::operator_decl_no_fixity);
5526-
5562+
else
55275563
res = new (Context) InfixOperatorDecl(CurDeclContext, OperatorLoc,
55285564
Name, NameLoc, colonLoc,
55295565
precedenceGroupName,
55305566
precedenceGroupNameLoc);
5531-
}
5567+
5568+
diagnoseOperatorFixityAttributes(*this, Attributes, res);
55325569

55335570
res->getAttrs() = Attributes;
55345571
return makeParserResult(res);

lib/Sema/TypeCheckAttr.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,14 +1085,6 @@ void AttributeChecker::checkOperatorAttribute(DeclAttribute *attr) {
10851085
return;
10861086
}
10871087

1088-
// Infix operator is only allowed on operator declarations, not on func.
1089-
if (isa<InfixAttr>(attr)) {
1090-
TC.diagnose(attr->getLocation(), diag::invalid_infix_on_func)
1091-
.fixItRemove(attr->getLocation());
1092-
attr->setInvalid();
1093-
return;
1094-
}
1095-
10961088
// Otherwise, must be unary.
10971089
if (!FD->isUnaryOperator()) {
10981090
TC.diagnose(attr->getLocation(), diag::attribute_requires_single_argument,

test/decl/func/operator.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ precedencegroup ReallyHighPrecedence {
1919
associativity: left
2020
}
2121

22-
infix func fn_binary(_ lhs: Int, rhs: Int) {} // expected-error {{'infix' requires a function with an operator identifier}}
22+
infix func fn_binary(_ lhs: Int, rhs: Int) {} // expected-error {{'infix' modifier is not required or allowed on func declarations}}
2323

2424

2525
func ++++(lhs: X, rhs: X) -> X {}
@@ -142,8 +142,10 @@ func test_14705150() {
142142

143143
}
144144

145-
prefix postfix func ++(x: Int) {} // expected-error {{attribute 'prefix' cannot be combined with this attribute}}
146-
postfix prefix func ++(x: Int) {} // expected-error {{attribute 'postfix' cannot be combined with this attribute}}
145+
prefix postfix func ++(x: Int) {} // expected-error {{'postfix' contradicts previous modifier 'prefix'}} {{8-16=}}
146+
postfix prefix func ++(x: Float) {} // expected-error {{'prefix' contradicts previous modifier 'postfix'}} {{9-16=}}
147+
postfix prefix infix func ++(x: Double) {} // expected-error {{'prefix' contradicts previous modifier 'postfix'}} {{9-16=}} expected-error {{'infix' contradicts previous modifier 'postfix'}} {{16-22=}}
148+
infix prefix func +-+(x: Int, y: Int) {} // expected-error {{'infix' modifier is not required or allowed on func declarations}} {{1-7=}} expected-error{{'prefix' contradicts previous modifier 'infix'}} {{7-14=}}
147149

148150
// Don't allow one to define a postfix '!'; it's built into the
149151
// language.

0 commit comments

Comments
 (0)