Skip to content

Commit 19f15ff

Browse files
committed
[NFC] Refactor attribute option parsing
1 parent 58c287c commit 19f15ff

File tree

2 files changed

+165
-112
lines changed

2 files changed

+165
-112
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1604,10 +1604,6 @@ ERROR(opened_attribute_id_value,none,
16041604
ERROR(opened_attribute_expected_rparen,none,
16051605
"expected ')' after id value for 'opened' attribute", ())
16061606

1607-
// effects
1608-
ERROR(effects_attribute_expect_option,none,
1609-
"expected '%0' option (readnone, readonly, readwrite)", (StringRef))
1610-
16111607
// unowned
16121608
ERROR(attr_unowned_invalid_specifier,none,
16131609
"expected 'safe' or 'unsafe'", ())

lib/Parse/ParseDecl.cpp

Lines changed: 165 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1735,6 +1735,133 @@ void Parser::parseAllAvailabilityMacroArguments() {
17351735
AvailabilityMacrosComputed = true;
17361736
}
17371737

1738+
/// Processes a parsed option name by attempting to match it to a list of
1739+
/// alternative name/value pairs provided by a chain of \c when() calls, ending
1740+
/// in either \c whenOmitted() if omitting the option is allowed, or
1741+
/// \c diagnoseWhenOmitted() if the option is mandatory.
1742+
template<typename T, typename R = T>
1743+
class LLVM_NODISCARD AttrOptionSwitch {
1744+
// Inputs:
1745+
Optional<StringRef> parsedName; // None: parse error, empty: omitted
1746+
Parser &P;
1747+
SourceLoc loc;
1748+
StringRef attrName; // empty: error already diagnosed
1749+
bool isDeclModifier;
1750+
1751+
// State:
1752+
StringRef exampleName; // empty: when() was never called
1753+
Optional<R> result; // None: no when() has matched
1754+
1755+
public:
1756+
/// \param parsedName The name of the option parsed out of the source code. If
1757+
/// \c None, there was some sort of parse error; this will normally be
1758+
/// diagnosed as \c diag::attr_expected_option_such_as using the name
1759+
/// from the first \c when() call as an example.
1760+
/// \param P The parser used to diagnose errors concerning this attribute
1761+
/// option.
1762+
/// \param loc The source location to diagnose errors at.
1763+
/// \param attrName The name of the attribute, used in diagnostics. If empty,
1764+
/// an error has already been diagnosed and the AttrOptionSwitch should
1765+
/// just fall through.
1766+
/// \param isDeclModifier Are we parsing an attribute or a modifier?
1767+
AttrOptionSwitch(Optional<StringRef> parsedName, Parser &P, SourceLoc loc,
1768+
StringRef attrName, bool isDeclModifier)
1769+
: parsedName(parsedName), P(P), loc(loc), attrName(attrName),
1770+
isDeclModifier(isDeclModifier) { }
1771+
1772+
/// If the option has the identifier \p name, give it value \p value.
1773+
AttrOptionSwitch<R, T> &when(StringLiteral name, T value) {
1774+
// Save this to use in a future diagnostic, if needed.
1775+
if (exampleName.empty() && !name.empty())
1776+
exampleName = name;
1777+
1778+
// Does this string match?
1779+
if (parsedName && *parsedName == name) {
1780+
assert(!result && "overlapping AttrOptionSwitch::when()s?");
1781+
result = std::move(value);
1782+
}
1783+
1784+
return *this;
1785+
}
1786+
1787+
/// Diagnose if the option is missing or was not matched, returning either the
1788+
/// option's value or \c None if an error was diagnosed.
1789+
Optional<R> diagnoseWhenOmitted() {
1790+
assert(!exampleName.empty() && "No AttrOptionSwitch::when() calls");
1791+
1792+
if (attrName.empty())
1793+
// An error has already been diagnosed; nothing to do.
1794+
return None;
1795+
1796+
if (!result) {
1797+
if (!parsedName)
1798+
// We parsed a non-identifier; diagnose it with `exampleName`.
1799+
P.diagnose(loc, diag::attr_expected_option_such_as, attrName,
1800+
exampleName);
1801+
else if (*parsedName == "")
1802+
// Option list was omitted; apparently this attr doesn't allow that.
1803+
P.diagnose(loc, diag::attr_expected_lparen, attrName, isDeclModifier);
1804+
else
1805+
// The identifier didn't match any of the when() calls.
1806+
P.diagnose(loc, diag::attr_unknown_option, *parsedName, attrName);
1807+
}
1808+
1809+
return result;
1810+
}
1811+
1812+
/// Diagnose if the option is missing or not matched, returning:
1813+
///
1814+
/// \returns \c None if an error was diagnosed; \p value if the option was
1815+
/// omitted; the value the option was matched to otherwise.
1816+
Optional<R> whenOmitted(T value) {
1817+
return when("", value).diagnoseWhenOmitted();
1818+
}
1819+
};
1820+
1821+
/// Parses an attribute argument list that allows a single identifier with a
1822+
/// known set of permitted options:
1823+
///
1824+
/// \verbatim
1825+
/// '(' identifier ')'
1826+
/// \endverbatim
1827+
///
1828+
/// Returns an object of type \c AttrOptionSwitch, a type loosely inspired by
1829+
/// \c llvm::StringSwitch which can be used in a fluent style to map each
1830+
/// permitted identifier to a value. Together, they will automatically
1831+
/// diagnose \c diag::attr_expected_lparen,
1832+
/// \c diag::attr_expected_option_such_as, \c diag::attr_unknown_option, and
1833+
/// \c diag::attr_expected_rparen when needed.
1834+
///
1835+
/// \seealso AttrOptionSwitch
1836+
template<typename R>
1837+
static AttrOptionSwitch<R>
1838+
parseSingleAttrOption(Parser &P, SourceLoc Loc, SourceRange &AttrRange,
1839+
StringRef AttrName, DeclAttrKind DK) {
1840+
bool isModifier = DeclAttribute::isDeclModifier(DK);
1841+
if (!P.consumeIf(tok::l_paren)) {
1842+
AttrRange = SourceRange(Loc);
1843+
// Create an AttrOptionSwitch with an empty value. The calls on it will
1844+
// decide whether or not that's valid.
1845+
return AttrOptionSwitch<R>(StringRef(), P, Loc, AttrName, isModifier);
1846+
}
1847+
1848+
StringRef parsedName = P.Tok.getText();
1849+
if (!P.consumeIf(tok::identifier)) {
1850+
// Once we have an example of a valid option, diagnose this with
1851+
// diag::attr_expected_option_such_as.
1852+
return AttrOptionSwitch<R>(None, P, Loc, AttrName, isModifier);
1853+
}
1854+
1855+
if (!P.consumeIf(tok::r_paren)) {
1856+
P.diagnose(Loc, diag::attr_expected_rparen, AttrName, isModifier);
1857+
// Pass through the switch without diagnosing anything.
1858+
return AttrOptionSwitch<R>(None, P, Loc, "", isModifier);
1859+
}
1860+
1861+
AttrRange = SourceRange(Loc, P.PreviousLoc);
1862+
return AttrOptionSwitch<R>(parsedName, P, Loc, AttrName, isModifier);
1863+
}
1864+
17381865
bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
17391866
DeclAttrKind DK, bool isFromClangAttribute) {
17401867
// Ok, it is a valid attribute, eat it, and then process it.
@@ -1814,114 +1941,49 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
18141941
#include "swift/AST/Attr.def"
18151942

18161943
case DAK_Effects: {
1817-
if (!consumeIf(tok::l_paren)) {
1818-
diagnose(Loc, diag::attr_expected_lparen, AttrName,
1819-
DeclAttribute::isDeclModifier(DK)); return false;
1820-
}
1821-
1822-
if (Tok.isNot(tok::identifier)) {
1823-
diagnose(Loc, diag::effects_attribute_expect_option, AttrName);
1824-
return false;
1825-
}
1826-
1827-
EffectsKind kind;
1828-
if (Tok.getText() == "readonly")
1829-
kind = EffectsKind::ReadOnly;
1830-
else if (Tok.getText() == "readnone")
1831-
kind = EffectsKind::ReadNone;
1832-
else if (Tok.getText() == "readwrite")
1833-
kind = EffectsKind::ReadWrite;
1834-
else if (Tok.getText() == "releasenone")
1835-
kind = EffectsKind::ReleaseNone;
1836-
else {
1837-
diagnose(Loc, diag::attr_unknown_option,
1838-
Tok.getText(), AttrName);
1839-
return false;
1840-
}
1841-
AttrRange = SourceRange(Loc, Tok.getRange().getStart());
1842-
consumeToken(tok::identifier);
1843-
1844-
if (!consumeIf(tok::r_paren)) {
1845-
diagnose(Loc, diag::attr_expected_rparen, AttrName,
1846-
DeclAttribute::isDeclModifier(DK));
1944+
auto kind = parseSingleAttrOption<EffectsKind>
1945+
(*this, Loc, AttrRange, AttrName, DK)
1946+
.when("readonly", EffectsKind::ReadOnly)
1947+
.when("readnone", EffectsKind::ReadNone)
1948+
.when("readwrite", EffectsKind::ReadWrite)
1949+
.when("releasenone", EffectsKind::ReleaseNone)
1950+
.diagnoseWhenOmitted();
1951+
if (!kind)
18471952
return false;
1848-
}
18491953

18501954
if (!DiscardAttribute)
1851-
Attributes.add(new (Context) EffectsAttr(AtLoc, AttrRange, kind));
1955+
Attributes.add(new (Context) EffectsAttr(AtLoc, AttrRange, *kind));
1956+
18521957
break;
18531958
}
18541959

18551960
case DAK_Inline: {
1856-
if (!consumeIf(tok::l_paren)) {
1857-
diagnose(Loc, diag::attr_expected_lparen, AttrName,
1858-
DeclAttribute::isDeclModifier(DK));
1961+
auto kind = parseSingleAttrOption<InlineKind>
1962+
(*this, Loc, AttrRange, AttrName, DK)
1963+
.when("never", InlineKind::Never)
1964+
.when("__always", InlineKind::Always)
1965+
.diagnoseWhenOmitted();
1966+
if (!kind)
18591967
return false;
1860-
}
1861-
1862-
if (Tok.isNot(tok::identifier)) {
1863-
diagnose(Loc, diag::attr_expected_option_such_as, AttrName, "none");
1864-
return false;
1865-
}
1866-
1867-
InlineKind kind;
1868-
if (Tok.getText() == "never")
1869-
kind = InlineKind::Never;
1870-
else if (Tok.getText() == "__always")
1871-
kind = InlineKind::Always;
1872-
else {
1873-
diagnose(Loc, diag::attr_unknown_option, Tok.getText(), AttrName);
1874-
return false;
1875-
}
1876-
consumeToken(tok::identifier);
1877-
AttrRange = SourceRange(Loc, Tok.getRange().getStart());
1878-
1879-
if (!consumeIf(tok::r_paren)) {
1880-
diagnose(Loc, diag::attr_expected_rparen, AttrName,
1881-
DeclAttribute::isDeclModifier(DK));
1882-
return false;
1883-
}
18841968

18851969
if (!DiscardAttribute)
1886-
Attributes.add(new (Context) InlineAttr(AtLoc, AttrRange, kind));
1970+
Attributes.add(new (Context) InlineAttr(AtLoc, AttrRange, *kind));
18871971

18881972
break;
18891973
}
18901974

18911975
case DAK_Optimize: {
1892-
if (!consumeIf(tok::l_paren)) {
1893-
diagnose(Loc, diag::attr_expected_lparen, AttrName,
1894-
DeclAttribute::isDeclModifier(DK));
1976+
auto optMode = parseSingleAttrOption<OptimizationMode>
1977+
(*this, Loc, AttrRange, AttrName, DK)
1978+
.when("speed", OptimizationMode::ForSpeed)
1979+
.when("size", OptimizationMode::ForSize)
1980+
.when("none", OptimizationMode::NoOptimization)
1981+
.diagnoseWhenOmitted();
1982+
if (!optMode)
18951983
return false;
1896-
}
1897-
1898-
if (Tok.isNot(tok::identifier)) {
1899-
diagnose(Loc, diag::attr_expected_option_such_as, AttrName, "speed");
1900-
return false;
1901-
}
1902-
1903-
OptimizationMode optMode = OptimizationMode::NotSet;
1904-
if (Tok.getText() == "none")
1905-
optMode = OptimizationMode::NoOptimization;
1906-
else if (Tok.getText() == "speed")
1907-
optMode = OptimizationMode::ForSpeed;
1908-
else if (Tok.getText() == "size")
1909-
optMode = OptimizationMode::ForSize;
1910-
else {
1911-
diagnose(Loc, diag::attr_unknown_option, Tok.getText(), AttrName);
1912-
return false;
1913-
}
1914-
consumeToken(tok::identifier);
1915-
AttrRange = SourceRange(Loc, Tok.getRange().getStart());
1916-
1917-
if (!consumeIf(tok::r_paren)) {
1918-
diagnose(Loc, diag::attr_expected_rparen, AttrName,
1919-
DeclAttribute::isDeclModifier(DK));
1920-
return false;
1921-
}
19221984

19231985
if (!DiscardAttribute)
1924-
Attributes.add(new (Context) OptimizeAttr(AtLoc, AttrRange, optMode));
1986+
Attributes.add(new (Context) OptimizeAttr(AtLoc, AttrRange, *optMode));
19251987

19261988
break;
19271989
}
@@ -1930,30 +1992,25 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
19301992
// Handle weak/unowned/unowned(unsafe).
19311993
auto Kind = AttrName == "weak" ? ReferenceOwnership::Weak
19321994
: ReferenceOwnership::Unowned;
1933-
SourceLoc EndLoc = Loc;
19341995

1935-
if (Kind == ReferenceOwnership::Unowned && Tok.is(tok::l_paren)) {
1996+
if (Kind == ReferenceOwnership::Unowned) {
19361997
// Parse an optional specifier after unowned.
1937-
SourceLoc lp = consumeToken(tok::l_paren);
1938-
if (Tok.is(tok::identifier) && Tok.getText() == "safe") {
1939-
consumeToken();
1940-
} else if (Tok.is(tok::identifier) && Tok.getText() == "unsafe") {
1941-
consumeToken();
1942-
Kind = ReferenceOwnership::Unmanaged;
1943-
} else {
1944-
diagnose(Tok, diag::attr_unowned_invalid_specifier);
1945-
consumeIf(tok::identifier);
1946-
}
1947-
1948-
SourceLoc rp;
1949-
parseMatchingToken(tok::r_paren, rp, diag::attr_unowned_expected_rparen,
1950-
lp);
1951-
EndLoc = rp;
1998+
Kind = parseSingleAttrOption<ReferenceOwnership>
1999+
(*this, Loc, AttrRange, AttrName, DK)
2000+
.when("unsafe", ReferenceOwnership::Unmanaged)
2001+
.when("safe", ReferenceOwnership::Unowned)
2002+
.whenOmitted(ReferenceOwnership::Unowned)
2003+
// Recover from errors by going back to Unowned.
2004+
.getValueOr(ReferenceOwnership::Unowned);
2005+
}
2006+
else {
2007+
AttrRange = SourceRange(Loc);
19522008
}
19532009

19542010
if (!DiscardAttribute)
19552011
Attributes.add(
1956-
new (Context) ReferenceOwnershipAttr(SourceRange(Loc, EndLoc), Kind));
2012+
new (Context) ReferenceOwnershipAttr(AttrRange, Kind));
2013+
19572014
break;
19582015
}
19592016

0 commit comments

Comments
 (0)