Skip to content

Commit b87cd54

Browse files
committed
gccrs: parse all function qualifiers before generating error
Instead of erroring out on the first misplaced qualifier, parse as many qualifiers as possible to allow for more helpful error message giving the correct order the qualifiers should be in. Duplicate qualifiers are a kind of a special case and generate a separate error message. gcc/rust/ChangeLog * parse/rust-parse-impl.hxx (parse_function_qualifiers) Collect qualifiers into vector before generating the error message * parse/rust-parse.h: (parse_function_qualifiers) Make function fallible gcc/testsuite/ChangeLog: * rust/compile/func-qualifier-default.rs: Adapt to change in compiler messages * rust/compile/func-qualifier-order.rs: Renamed existing test file for clarity (from) * rust/compile/func-qualifier-order-1.rs: Renamed existing test file (to) * rust/compile/func-qualifier-order-2.rs: New test file * rust/compile/func-qualifier-order-3.rs: New test file Signed-off-by: Philipp Gesang <phg@phi-gamma.net>
1 parent ca98ef5 commit b87cd54

File tree

6 files changed

+176
-45
lines changed

6 files changed

+176
-45
lines changed

gcc/rust/parse/rust-parse-impl.hxx

Lines changed: 153 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1559,7 +1559,10 @@ Parser<ManagedTokenSource>::parse_function (AST::Visibility vis,
15591559
{
15601560
location_t locus = lexer.peek_token ()->get_locus ();
15611561
// Get qualifiers for function if they exist
1562-
AST::FunctionQualifiers qualifiers = parse_function_qualifiers ();
1562+
std::unique_ptr<AST::FunctionQualifiers> qualifiers
1563+
= parse_function_qualifiers ();
1564+
if (qualifiers == nullptr)
1565+
return nullptr;
15631566

15641567
skip_token (FN_KW);
15651568

@@ -1636,7 +1639,7 @@ Parser<ManagedTokenSource>::parse_function (AST::Visibility vis,
16361639
}
16371640

16381641
return std::unique_ptr<AST::Function> (
1639-
new AST::Function (std::move (function_name), std::move (qualifiers),
1642+
new AST::Function (std::move (function_name), std::move (*qualifiers),
16401643
std::move (generic_params), std::move (function_params),
16411644
std::move (return_type), std::move (where_clause),
16421645
std::move (body), std::move (vis),
@@ -1645,7 +1648,7 @@ Parser<ManagedTokenSource>::parse_function (AST::Visibility vis,
16451648

16461649
// Parses function or method qualifiers (i.e. const, unsafe, and extern).
16471650
template <typename ManagedTokenSource>
1648-
AST::FunctionQualifiers
1651+
std::unique_ptr<AST::FunctionQualifiers>
16491652
Parser<ManagedTokenSource>::parse_function_qualifiers ()
16501653
{
16511654
Default default_status = Default::No;
@@ -1655,51 +1658,151 @@ Parser<ManagedTokenSource>::parse_function_qualifiers ()
16551658
bool has_extern = false;
16561659
std::string abi;
16571660

1661+
// collect all qualifiers before checking the order to allow for a better
1662+
// error message
1663+
std::vector<TokenId> found_order;
1664+
16581665
const_TokenPtr t;
16591666
location_t locus = lexer.peek_token ()->get_locus ();
1660-
// Check in order of default, const, async, unsafe, extern
1661-
if (lexer.peek_token ()->get_id () == IDENTIFIER
1662-
&& lexer.peek_token ()->get_str () == Values::WeakKeywords::DEFAULT)
1663-
{
1664-
default_status = Default::Yes;
1665-
lexer.skip_token();
1666-
}
16671667

1668-
if (lexer.peek_token ()->get_id () == CONST)
1668+
// this will terminate on duplicates or the first non-qualifier token
1669+
while (true)
16691670
{
1670-
lexer.skip_token ();
1671-
const_status = Const::Yes;
1672-
}
1671+
const TokenId token_id = lexer.peek_token ()->get_id ();
16731672

1674-
if (lexer.peek_token ()->get_id () == ASYNC)
1675-
{
1676-
lexer.skip_token ();
1677-
async_status = Async::Yes;
1678-
}
1673+
if (std::find (found_order.cbegin (), found_order.cend (), token_id)
1674+
!= found_order.cend ())
1675+
{
1676+
// qualifiers mustn't appear twice
1677+
Error error (lexer.peek_token ()->get_locus (),
1678+
"encountered duplicate function qualifier %qs",
1679+
lexer.peek_token ()->get_token_description ());
1680+
add_error (std::move (error));
16791681

1680-
if (lexer.peek_token ()->get_id () == UNSAFE)
1681-
{
1682-
lexer.skip_token ();
1683-
unsafe_status = Unsafety::Unsafe;
1684-
}
1682+
return nullptr;
1683+
}
16851684

1686-
if (lexer.peek_token ()->get_id () == EXTERN_KW)
1687-
{
1685+
switch (token_id)
1686+
{
1687+
default:
1688+
goto done;
1689+
case IDENTIFIER:
1690+
if (lexer.peek_token ()->get_str () != Values::WeakKeywords::DEFAULT)
1691+
{
1692+
// only "default" is valid in this context
1693+
goto done;
1694+
}
1695+
default_status = Default::Yes;
1696+
break;
1697+
case CONST:
1698+
const_status = Const::Yes;
1699+
break;
1700+
case ASYNC:
1701+
async_status = Async::Yes;
1702+
break;
1703+
case UNSAFE:
1704+
unsafe_status = Unsafety::Unsafe;
1705+
break;
1706+
case EXTERN_KW:
1707+
has_extern = true;
1708+
// detect optional abi name
1709+
lexer.skip_token ();
1710+
const_TokenPtr next_tok = lexer.peek_token ();
1711+
if (next_tok->get_id () == STRING_LITERAL)
1712+
{
1713+
abi = next_tok->get_str ();
1714+
}
1715+
break;
1716+
}
1717+
found_order.push_back (token_id);
16881718
lexer.skip_token ();
1689-
has_extern = true;
1719+
}
1720+
done:
16901721

1691-
// detect optional abi name
1692-
const_TokenPtr next_tok = lexer.peek_token ();
1693-
if (next_tok->get_id () == STRING_LITERAL)
1722+
// Check in order of default, const, async, unsafe, extern
1723+
auto token_priority = [] (const TokenId id) {
1724+
switch (id)
1725+
{
1726+
default: // unreachable, but will cause an error
1727+
return 0;
1728+
case IDENTIFIER: // "default"; the only "weak" keyword considered here
1729+
return 1;
1730+
case CONST:
1731+
return 2;
1732+
case ASYNC:
1733+
return 3;
1734+
case UNSAFE:
1735+
return 4;
1736+
case EXTERN_KW:
1737+
return 5;
1738+
};
1739+
};
1740+
1741+
size_t last_priority = 0;
1742+
for (auto token_id : found_order)
1743+
{
1744+
const size_t priority = token_priority (token_id);
1745+
if (priority <= last_priority)
16941746
{
1695-
lexer.skip_token ();
1696-
abi = next_tok->get_str ();
1747+
auto qualifiers_to_str = [] (const std::vector<TokenId> &token_ids) {
1748+
std::string acc;
1749+
1750+
for (auto id : token_ids)
1751+
{
1752+
if (!acc.empty ())
1753+
acc += ' ';
1754+
1755+
if (id == IDENTIFIER)
1756+
acc += "default";
1757+
else
1758+
acc += token_id_keyword_string (id);
1759+
}
1760+
1761+
return acc;
1762+
};
1763+
1764+
std::vector<TokenId> expected_order
1765+
= {IDENTIFIER, CONST, ASYNC, UNSAFE, EXTERN_KW};
1766+
1767+
// we only keep the qualifiers actually used in the offending code
1768+
std::vector<TokenId>::const_iterator token_id
1769+
= expected_order.cbegin ();
1770+
while (token_id != expected_order.cend ())
1771+
{
1772+
if (std::find (found_order.cbegin (), found_order.cend (),
1773+
*token_id)
1774+
== found_order.cend ())
1775+
{
1776+
token_id = expected_order.erase (token_id);
1777+
}
1778+
else
1779+
{
1780+
++token_id;
1781+
}
1782+
}
1783+
1784+
const std::string found_qualifiers = qualifiers_to_str (found_order);
1785+
const std::string expected_qualifiers
1786+
= qualifiers_to_str (expected_order);
1787+
1788+
location_t error_locus
1789+
= make_location (locus, locus, lexer.peek_token ()->get_locus ());
1790+
Error error (
1791+
error_locus,
1792+
"invalid order of function qualifiers; found %qs, expected %qs",
1793+
found_qualifiers.c_str (), expected_qualifiers.c_str ());
1794+
add_error (std::move (error));
1795+
1796+
return nullptr;
16971797
}
1798+
1799+
last_priority = priority;
16981800
}
16991801

1700-
return AST::FunctionQualifiers (locus, default_status, async_status,
1701-
const_status, unsafe_status, has_extern,
1702-
std::move (abi));
1802+
return std::unique_ptr<AST::FunctionQualifiers> (
1803+
new AST::FunctionQualifiers (locus, default_status, async_status,
1804+
const_status, unsafe_status, has_extern,
1805+
std::move (abi)));
17031806
}
17041807

17051808
// Parses generic (lifetime or type) params inside angle brackets (optional).
@@ -4276,7 +4379,10 @@ Parser<ManagedTokenSource>::parse_inherent_impl_function_or_method (
42764379
{
42774380
location_t locus = lexer.peek_token ()->get_locus ();
42784381
// parse function or method qualifiers
4279-
AST::FunctionQualifiers qualifiers = parse_function_qualifiers ();
4382+
std::unique_ptr<AST::FunctionQualifiers> qualifiers
4383+
= parse_function_qualifiers ();
4384+
if (qualifiers == nullptr)
4385+
return nullptr;
42804386

42814387
skip_token (FN_KW);
42824388

@@ -4362,7 +4468,7 @@ Parser<ManagedTokenSource>::parse_inherent_impl_function_or_method (
43624468
}
43634469

43644470
return std::unique_ptr<AST::Function> (
4365-
new AST::Function (std::move (ident), std::move (qualifiers),
4471+
new AST::Function (std::move (ident), std::move (*qualifiers),
43664472
std::move (generic_params), std::move (function_params),
43674473
std::move (return_type), std::move (where_clause),
43684474
std::move (body), std::move (vis),
@@ -4459,7 +4565,10 @@ Parser<ManagedTokenSource>::parse_trait_impl_function_or_method (
44594565
location_t locus = lexer.peek_token ()->get_locus ();
44604566

44614567
// parse function or method qualifiers
4462-
AST::FunctionQualifiers qualifiers = parse_function_qualifiers ();
4568+
std::unique_ptr<AST::FunctionQualifiers> qualifiers
4569+
= parse_function_qualifiers ();
4570+
if (qualifiers == nullptr)
4571+
return nullptr;
44634572

44644573
skip_token (FN_KW);
44654574

@@ -4590,7 +4699,7 @@ Parser<ManagedTokenSource>::parse_trait_impl_function_or_method (
45904699
}
45914700

45924701
return std::unique_ptr<AST::Function> (
4593-
new AST::Function (std::move (ident), std::move (qualifiers),
4702+
new AST::Function (std::move (ident), std::move (*qualifiers),
45944703
std::move (generic_params), std::move (function_params),
45954704
std::move (return_type), std::move (where_clause),
45964705
std::move (body), std::move (vis),
@@ -6202,7 +6311,10 @@ Parser<ManagedTokenSource>::parse_bare_function_type (
62026311
// TODO: pass in for lifetime location as param
62036312
location_t best_try_locus = lexer.peek_token ()->get_locus ();
62046313

6205-
AST::FunctionQualifiers qualifiers = parse_function_qualifiers ();
6314+
std::unique_ptr<AST::FunctionQualifiers> qualifiers
6315+
= parse_function_qualifiers ();
6316+
if (qualifiers == nullptr)
6317+
return nullptr;
62066318

62076319
if (!skip_token (FN_KW))
62086320
return nullptr;
@@ -6287,7 +6399,7 @@ Parser<ManagedTokenSource>::parse_bare_function_type (
62876399

62886400
return std::unique_ptr<AST::BareFunctionType> (
62896401
new AST::BareFunctionType (std::move (for_lifetimes),
6290-
std::move (qualifiers), std::move (params),
6402+
std::move (*qualifiers), std::move (params),
62916403
is_variadic, std::move (variadic_attrs),
62926404
std::move (return_type), best_try_locus));
62936405
}

gcc/rust/parse/rust-parse.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ template <typename ManagedTokenSource> class Parser
364364
std::unique_ptr<AST::Function> parse_function (AST::Visibility vis,
365365
AST::AttrVec outer_attrs,
366366
bool is_external = false);
367-
AST::FunctionQualifiers parse_function_qualifiers ();
367+
std::unique_ptr<AST::FunctionQualifiers> parse_function_qualifiers ();
368368
std::vector<std::unique_ptr<AST::GenericParam>>
369369
parse_generic_params_in_angles ();
370370
template <typename EndTokenPred>

gcc/testsuite/rust/compile/func-qualifier-default.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ impl<T> Trait for T {
1212

1313
// `default` precedes all other qualifiers
1414
unsafe default fn unsafe_fn() {}
15-
// { dg-error "expecting .fn. but .identifier. found" "" { target *-*-* } .-1 }
16-
// { dg-error "expecting ... but .fn. found" "" { target *-*-* } .-2 }
17-
// { dg-error "failed to parse trait impl item in trait impl" "" { target *-*-* } .-3 }
15+
// { dg-error "invalid order of function qualifiers; found .unsafe default., expected .default unsafe." "" { target *-*-* } .-1 }
16+
// { dg-error "failed to parse trait impl item in trait impl" "" { target *-*-* } .-2 }
1817
}
File renamed without changes.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// { dg-additional-options "-frust-edition=2018" }
2+
#![feature(no_core)]
3+
#![no_core]
4+
5+
async unsafe async fn duplicate_qualifier() {} // { dg-error "encountered duplicate function qualifier .async." }
6+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#![feature(min_specialization)]
2+
#![feature(no_core)]
3+
#![no_core]
4+
5+
trait Dummy {
6+
}
7+
8+
// the purpose of this is to trigger the compiler message
9+
// regarding the order of qualifiers
10+
impl<T> Dummy for T {
11+
default async unsafe extern "C" const fn all_the_qualifiers() {}
12+
// { dg-error "invalid order of function qualifiers; found .default async unsafe extern const., expected .default const async unsafe extern." "" { target *-*-* } .-1 }
13+
// { dg-error "failed to parse trait impl item in trait impl" "" { target *-*-* } .-2 }
14+
}

0 commit comments

Comments
 (0)