get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/2218070/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2218070,
    "url": "http://patchwork.ozlabs.org/api/patches/2218070/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/gcc/patch/20260331104730.3162-5-gerris.rs@gmail.com/",
    "project": {
        "id": 17,
        "url": "http://patchwork.ozlabs.org/api/projects/17/?format=api",
        "name": "GNU Compiler Collection",
        "link_name": "gcc",
        "list_id": "gcc-patches.gcc.gnu.org",
        "list_email": "gcc-patches@gcc.gnu.org",
        "web_url": null,
        "scm_url": null,
        "webscm_url": null,
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20260331104730.3162-5-gerris.rs@gmail.com>",
    "list_archive_url": null,
    "date": "2026-03-31T10:47:30",
    "name": "[gccrs,COMMIT,5/5] gccrs: refactor function qualifier parsing",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "b00b8af12d7b43009444d14a8b9c912aac5672b2",
    "submitter": {
        "id": 92092,
        "url": "http://patchwork.ozlabs.org/api/people/92092/?format=api",
        "name": null,
        "email": "gerris.rs@gmail.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/gcc/patch/20260331104730.3162-5-gerris.rs@gmail.com/mbox/",
    "series": [
        {
            "id": 498172,
            "url": "http://patchwork.ozlabs.org/api/series/498172/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/gcc/list/?series=498172",
            "date": "2026-03-31T10:47:28",
            "name": "[gccrs,COMMIT,1/5] Add `default` as function qualifier.",
            "version": 1,
            "mbox": "http://patchwork.ozlabs.org/series/498172/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2218070/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2218070/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org>",
        "X-Original-To": [
            "incoming@patchwork.ozlabs.org",
            "gcc-patches@gcc.gnu.org"
        ],
        "Delivered-To": [
            "patchwork-incoming@legolas.ozlabs.org",
            "gcc-patches@gcc.gnu.org"
        ],
        "Authentication-Results": [
            "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=PlMtimwm;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org\n (client-ip=2620:52:6:3111::32; helo=vm01.sourceware.org;\n envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org;\n receiver=patchwork.ozlabs.org)",
            "sourceware.org;\n\tdkim=pass (2048-bit key,\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=PlMtimwm",
            "sourceware.org;\n dmarc=pass (p=none dis=none) header.from=gmail.com",
            "sourceware.org; spf=pass smtp.mailfrom=gmail.com",
            "server2.sourceware.org;\n arc=none smtp.remote-ip=209.85.210.46"
        ],
        "Received": [
            "from vm01.sourceware.org (vm01.sourceware.org\n [IPv6:2620:52:6:3111::32])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4flPxX0gqtz1xtJ\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 31 Mar 2026 21:49:04 +1100 (AEDT)",
            "from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id 4468B4BB58F5\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 31 Mar 2026 10:49:02 +0000 (GMT)",
            "from mail-ot1-f46.google.com (mail-ot1-f46.google.com\n [209.85.210.46])\n by sourceware.org (Postfix) with ESMTPS id 995024B92099\n for <gcc-patches@gcc.gnu.org>; Tue, 31 Mar 2026 10:47:38 +0000 (GMT)",
            "by mail-ot1-f46.google.com with SMTP id\n 46e09a7af769-7d9e22176a7so2818741a34.1\n for <gcc-patches@gcc.gnu.org>; Tue, 31 Mar 2026 03:47:38 -0700 (PDT)",
            "from\n runnervmrg6be.xariblbjf3ru1nzambwxmzlesd.gx.internal.cloudapp.net\n ([52.173.123.150]) by smtp.gmail.com with ESMTPSA id\n 46e09a7af769-7da0a819880sm7914900a34.22.2026.03.31.03.47.36\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Tue, 31 Mar 2026 03:47:37 -0700 (PDT)"
        ],
        "DKIM-Filter": [
            "OpenDKIM Filter v2.11.0 sourceware.org 4468B4BB58F5",
            "OpenDKIM Filter v2.11.0 sourceware.org 995024B92099"
        ],
        "DMARC-Filter": "OpenDMARC Filter v1.4.2 sourceware.org 995024B92099",
        "ARC-Filter": "OpenARC Filter v1.0.0 sourceware.org 995024B92099",
        "ARC-Seal": "i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1774954058; cv=none;\n b=GuylUn/+QQHGsQrF//QbeRfaKAGSPU7XbMNc4+S6w4hGfJm03s5bPZXUATtAnUkbn8b7R+iG4A/EWWsvX9CieKm9IoiA+hVRO90goZCxW8wVxtJ1XN87WVoOlkWFpQRxkTpt7+R7TnK2SXelrE85UQGoEasCspdCC0ScmzRUj+U=",
        "ARC-Message-Signature": "i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1774954058; c=relaxed/simple;\n bh=brL/xEIeRKclBKj7Ifmae8Eg/eKFettQ9kYbLdPChoQ=;\n h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version;\n b=WV/Wpf99pybcBNSW/x77IkbWa/1r4BC9kgvaP2nnbFwnGcv2AXzAgFGIsWATzvwcFGHRTfo+MPwYVLk6KRGiO0ylXIftM93jWUYC70gmAFQLAmt0cLAnRyLYVyxzpkXRr2GOhN526kD/xafrZKv7GjtwuhbWgb/bvf1X2+rh+NY=",
        "ARC-Authentication-Results": "i=1; server2.sourceware.org",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=gmail.com; s=20251104; t=1774954058; x=1775558858; darn=gcc.gnu.org;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n :message-id:reply-to;\n bh=eTN5zIe2jN7pwDp4FcKf+P6WtRVKj7+q5187Xqa9iCo=;\n b=PlMtimwmfT6GXJe5PmlsYBfgUCPpLAzUh4DKpFp6kbmAMo2a2eTT2KD6AI1iyf0RC6\n XkS/BVL3H4KNJLgEAhZyQ8S61mEc0GqbHZuFG7IOjVaAjcNMyFxjQ1yWQ4ozB6uRNl6X\n zVCfjwcifyehgn7GqJgj/jESBTG6RAMDGkpKJkweFez83V1vA/L+xTPgrcYjO9PG2Qdu\n ERX0ylrGvesfvl3ILtxcYFqNeI/46tTWxkdigGtMLtyY+i8FbNVSCrmen73q61C8/ssN\n VjZmk8Ui1Z/+yBS2SvFz4+byfQCgdyNXOCyEpPwyjKQjRMrHXiJT23uo45NnVnRhH1xX\n EjbA==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1774954058; x=1775558858;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from\n :to:cc:subject:date:message-id:reply-to;\n bh=eTN5zIe2jN7pwDp4FcKf+P6WtRVKj7+q5187Xqa9iCo=;\n b=j1cA4c0kb9L0ezcCY6HAuQ7xE8eOHAwHKLqdDtWXAy1uP/H966uNP3+QwXAveXG9YW\n i5CHh+DrVTeFHgLyTWlualrOI5rJhR2QQmljTPJS/0DsgSDiX64F80DC0wzWFLdq9FBn\n vZuAAwcr+gc11JkyRBElTbjwsDNACfdwHUs7vssV4Fw6ZAx+EKXwEnReg9chMqNmsfxz\n BB2JZDLZGsQ+CJoecKl9Xjarkan6OcSmg1EegKeXk7EjmUlqmYql2Azbw6H56E2HhNmk\n 56+9MXxwJrl9xWgaDoSblM0I19c2xfkHxy6rqcEUXP2nmuk10WsZ/QRA/PHgIuLMJBM/\n 0IdQ==",
        "X-Gm-Message-State": "AOJu0Yx1k9NgSsCsmJ5/D402jjV2Z5CRY7VZQINZ9XJlZkJHkPKoRLz6\n mBw6GjAPIlqqQ7hYt1TC6sQtxaLjD/C2m0jW2hz0NZNWmJSjHnyZGp356JL1mRHp",
        "X-Gm-Gg": "ATEYQzxmN69oK+lkGRGr79aeYFJr/ORpJtlVp4aaSgLFvyDK8ohKZB5bJd7XhZuRlEd\n KXMgIJ69+2xPQ/mmZE6LpubaS2rH0HydtxpjxPi06LYnHSTCVvVI7uvp0Pz0J0QZFFAq4o3aP7A\n YIO4qKvn+mIX9xGOSAFhUnVqSAxdAYSQtBi/sYkCz8L6c/zFIVyqpqWoNTlSUjxMOcKWRgw5B9h\n UrzXedRL5tE6GaK9537pp2QAIUDOgecbqYaT2vY9NFv4Tp7QGmwNVG9RC/SV30BunUheR61ohLI\n 8Py4Klm+/LpIUXQHkc6Yex5xUWT+g7Z88jIZYeux3Gg6dwl2wxgcDVxb1Z9J+yKtA3IDOQmmosx\n BjS9qsqZFu9nCjBODIU93/EI1+/FnkqxTN4p+n86hpwyqqOLG92vo0PkzJkHg4keh2hyzQ+Ub9o\n YpcTjsRIBdq2NKL25iHV9CS5iMrhmH3rq7jjGLgL0cguSnPNadQ+wH4DISCAUtwOo+Fdt2hftkF\n o3JXQZScY6YETbv6/MdIYs=",
        "X-Received": "by 2002:a05:6830:710f:b0:7d7:e3b9:58dd with SMTP id\n 46e09a7af769-7d9fae230b2mr8545916a34.10.1774954057750;\n Tue, 31 Mar 2026 03:47:37 -0700 (PDT)",
        "From": "gerris.rs@gmail.com",
        "To": "gcc-patches@gcc.gnu.org,\n\tgcc-rust@gcc.gnu.org",
        "Cc": "pierre-emmanuel.patry@embecosm.com, arthur.cohen@embecosm.com,\n dkm+gccrs@kataplop.net, Philipp Gesang <phg@phi-gamma.net>",
        "Subject": "[gccrs COMMIT 5/5] gccrs: refactor function qualifier parsing",
        "Date": "Tue, 31 Mar 2026 10:47:30 +0000",
        "Message-ID": "<20260331104730.3162-5-gerris.rs@gmail.com>",
        "X-Mailer": "git-send-email 2.43.0",
        "In-Reply-To": "<20260331104730.3162-1-gerris.rs@gmail.com>",
        "References": "<20260331104730.3162-1-gerris.rs@gmail.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "X-BeenThere": "gcc-patches@gcc.gnu.org",
        "X-Mailman-Version": "2.1.30",
        "Precedence": "list",
        "List-Id": "Gcc-patches mailing list <gcc-patches.gcc.gnu.org>",
        "List-Unsubscribe": "<https://gcc.gnu.org/mailman/options/gcc-patches>,\n <mailto:gcc-patches-request@gcc.gnu.org?subject=unsubscribe>",
        "List-Archive": "<https://gcc.gnu.org/pipermail/gcc-patches/>",
        "List-Post": "<mailto:gcc-patches@gcc.gnu.org>",
        "List-Help": "<mailto:gcc-patches-request@gcc.gnu.org?subject=help>",
        "List-Subscribe": "<https://gcc.gnu.org/mailman/listinfo/gcc-patches>,\n <mailto:gcc-patches-request@gcc.gnu.org?subject=subscribe>",
        "Errors-To": "gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org"
    },
    "content": "From: Philipp Gesang <phg@phi-gamma.net>\n\ngcc/rust/ChangeLog\n\t* parse/rust-parse-impl.hxx: Refactor qualifier parsing\n\t* parse/rust-parse.h: (parse_function_qualifiers) Likewise\n\nSigned-off-by: Philipp Gesang <phg@phi-gamma.net>\n---\nThis change was merged into the gccrs repository and is posted here for\nupstream visibility and potential drive-by review, as requested by GCC\nrelease managers.\nEach commit email contains a link to its details on github from where you can\nfind the Pull-Request and associated discussions.\n\n\nCommit on github: https://github.com/Rust-GCC/gccrs/commit/732732618557f25d87885d816d75f456fe3b03a3\n\nThe commit has been mentioned in the following pull-request(s):\n - https://github.com/Rust-GCC/gccrs/pull/4463\n\n gcc/rust/parse/rust-parse-impl.hxx | 236 ++++++++++++++++++-----------\n gcc/rust/parse/rust-parse.h        |  16 +-\n 2 files changed, 159 insertions(+), 93 deletions(-)",
    "diff": "diff --git a/gcc/rust/parse/rust-parse-impl.hxx b/gcc/rust/parse/rust-parse-impl.hxx\nindex fc9f45a8e..97bd5b15e 100644\n--- a/gcc/rust/parse/rust-parse-impl.hxx\n+++ b/gcc/rust/parse/rust-parse-impl.hxx\n@@ -1638,7 +1638,7 @@ Parser<ManagedTokenSource>::parse_function (AST::Visibility vis,\n     }\n \n   return std::unique_ptr<AST::Function> (new AST::Function (\n-    std::move (function_name), std::move (*qualifiers.value ()),\n+    std::move (function_name), std::move (qualifiers.value ()),\n     std::move (generic_params), std::move (function_params),\n     std::move (return_type), std::move (where_clause), std::move (body),\n     std::move (vis), std::move (outer_attrs), locus, is_external));\n@@ -1646,93 +1646,146 @@ Parser<ManagedTokenSource>::parse_function (AST::Visibility vis,\n \n // Parses function or method qualifiers (i.e. const, unsafe, and extern).\n template <typename ManagedTokenSource>\n-tl::expected<std::unique_ptr<AST::FunctionQualifiers>, Parse::Error::Node>\n+tl::expected<AST::FunctionQualifiers, Parse::Error::Node>\n Parser<ManagedTokenSource>::parse_function_qualifiers ()\n+{\n+  location_t locus = lexer.peek_token ()->get_locus ();\n+\n+  auto parsed = parse_function_qualifiers_raw (locus);\n+  if (!parsed)\n+    return tl::unexpected<Parse::Error::Node> (parsed.error ());\n+\n+  return function_qualifiers_from_keywords (locus, std::move (parsed->first),\n+\t\t\t\t\t    std::move (parsed->second));\n+}\n+\n+// Take the list of parsed function qualifiers and convert it to\n+// the corrresponding flags to pass to the AST item constructor.\n+//\n+// This assumes ``keywords`` contains only those tokens that\n+// map to qualifiers.\n+template <typename ManagedTokenSource>\n+tl::expected<AST::FunctionQualifiers, Parse::Error::Node>\n+Parser<ManagedTokenSource>::function_qualifiers_from_keywords (\n+  location_t locus, const std::vector<TokenId> keywords, std::string abi)\n {\n   Default default_status = Default::No;\n   Async async_status = Async::No;\n   Const const_status = Const::No;\n   Unsafety unsafe_status = Unsafety::Normal;\n   bool has_extern = false;\n-  std::string abi;\n \n-  // collect all qualifiers before checking the order to allow for a better\n-  // error message\n-  std::vector<TokenId> found_order;\n+  for (auto qualifier : keywords)\n+    {\n+      switch (qualifier)\n+\t{\n+\tcase IDENTIFIER:\n+\t  // only \"default\" is valid in this context\n+\t  default_status = Default::Yes;\n+\t  continue;\n+\tcase CONST:\n+\t  const_status = Const::Yes;\n+\t  continue;\n+\tcase ASYNC:\n+\t  async_status = Async::Yes;\n+\t  continue;\n+\tcase UNSAFE:\n+\t  unsafe_status = Unsafety::Unsafe;\n+\t  continue;\n+\tcase EXTERN_KW:\n+\t  has_extern = true;\n+\t  continue;\n+\tdefault:\n+\t  // non-qualifier token in input\n+\t  rust_unreachable ();\n+\t}\n+    }\n \n-  const_TokenPtr t;\n-  location_t locus = lexer.peek_token ()->get_locus ();\n+  return AST::FunctionQualifiers (locus, default_status, async_status,\n+\t\t\t\t  const_status, unsafe_status, has_extern,\n+\t\t\t\t  std::move (abi));\n+}\n+\n+// this consumes as many function qualifier tokens while ensuring\n+// uniqueness.\n+template <typename ManagedTokenSource>\n+tl::expected<std::pair<std::vector<TokenId>, std::string>, Parse::Error::Node>\n+Parser<ManagedTokenSource>::parse_function_qualifiers_raw (location_t locus)\n+{\n+  std::vector<TokenId> found_order;\n+  std::string abi;\n \n   // this will terminate on duplicates or the first non-qualifier token\n   while (true)\n     {\n-      const TokenId token_id = lexer.peek_token ()->get_id ();\n-\n-      if (std::find (found_order.cbegin (), found_order.cend (), token_id)\n-\t  != found_order.cend ())\n-\t{\n-\t  // qualifiers mustn't appear twice\n-\t  Error error (lexer.peek_token ()->get_locus (),\n-\t\t       \"encountered duplicate function qualifier %qs\",\n-\t\t       lexer.peek_token ()->get_token_description ());\n-\t  add_error (std::move (error));\n-\n-\t  return tl::unexpected<Parse::Error::Node> (\n-\t    Parse::Error::Node::MALFORMED);\n-\t}\n+      auto token = lexer.peek_token ();\n+      const TokenId token_id = token->get_id ();\n+      location_t locus = lexer.peek_token ()->get_locus ();\n \n       switch (token_id)\n \t{\n \tcase IDENTIFIER:\n-\t  if (lexer.peek_token ()->get_str () != Values::WeakKeywords::DEFAULT)\n+\t  if (token->get_str () != Values::WeakKeywords::DEFAULT)\n \t    {\n-\t      // only \"default\" is valid in this context\n+\t      // only \"default\" is valid in this context, so this must\n+\t      // be a non-qualifier keyword\n \t      goto done;\n \t    }\n-\t  default_status = Default::Yes;\n-\t  break;\n+\t  // fallthrough\n \tcase CONST:\n-\t  const_status = Const::Yes;\n-\t  break;\n \tcase ASYNC:\n-\t  async_status = Async::Yes;\n-\t  break;\n \tcase UNSAFE:\n-\t  unsafe_status = Unsafety::Unsafe;\n+\t  found_order.push_back (token_id);\n+\t  lexer.skip_token ();\n \t  break;\n \tcase EXTERN_KW:\n \t  {\n-\t    has_extern = true;\n-\t    // detect optional abi name\n+\t    found_order.push_back (token_id);\n \t    lexer.skip_token ();\n+\n+\t    // detect optional abi name\n \t    const_TokenPtr next_tok = lexer.peek_token ();\n \t    if (next_tok->get_id () == STRING_LITERAL)\n \t      {\n \t\tabi = next_tok->get_str ();\n+\t\tlexer.skip_token ();\n \t      }\n \t  }\n \t  break;\n \tdefault:\n+\t  // non-qualifier keyword\n \t  goto done;\n \t}\n-      found_order.push_back (token_id);\n-      lexer.skip_token ();\n+\n+      if (std::count (found_order.cbegin (), found_order.cend (), token_id) > 1)\n+\t{\n+\t  // qualifiers mustn't appear twice\n+\t  Error error (locus, \"encountered duplicate function qualifier %qs\",\n+\t\t       token->get_token_description ());\n+\t  add_error (std::move (error));\n+\n+\t  return tl::unexpected<Parse::Error::Node> (\n+\t    Parse::Error::Node::MALFORMED);\n+\t}\n     }\n done:\n \n-  if (!ensure_function_qualifier_order (locus, std::move (found_order)))\n+  if (!ensure_function_qualifier_order (locus, found_order))\n     return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);\n \n-  return std::unique_ptr<AST::FunctionQualifiers> (\n-    new AST::FunctionQualifiers (locus, default_status, async_status,\n-\t\t\t\t const_status, unsafe_status, has_extern,\n-\t\t\t\t std::move (abi)));\n+  return make_pair (found_order, abi);\n }\n \n+// Validate the order of the list of function qualifiers; this assumes that\n+// ``found_order`` consists only of function qualifier tokens.\n+//\n+// If the order is illegal, the generated error message gives both the wrong\n+// order as found in the source and the correct order according to Rust syntax\n+// rules.\n template <typename ManagedTokenSource>\n bool\n Parser<ManagedTokenSource>::ensure_function_qualifier_order (\n-  location_t locus, std::vector<TokenId> found_order)\n+  location_t locus, const std::vector<TokenId> &found_order)\n {\n   // Check in order of default, const, async, unsafe, extern\n   auto token_priority = [] (const TokenId id) {\n@@ -1759,62 +1812,65 @@ Parser<ManagedTokenSource>::ensure_function_qualifier_order (\n       const size_t priority = token_priority (token_id);\n       if (priority <= last_priority)\n \t{\n-\t  auto qualifiers_to_str = [] (const std::vector<TokenId> &token_ids) {\n-\t    std::ostringstream ss;\n+\t  emit_function_qualifier_order_error_msg (locus, found_order);\n+\t  return false;\n+\t}\n \n-\t    for (auto id : token_ids)\n-\t      {\n-\t\tif (ss.tellp () != 0)\n-\t\t  ss << ' ';\n+      last_priority = priority;\n+    }\n \n-\t\tif (id == IDENTIFIER)\n-\t\t  ss << Values::WeakKeywords::DEFAULT;\n-\t\telse\n-\t\t  ss << token_id_keyword_string (id);\n-\t      }\n+  return true;\n+}\n \n-\t    return ss.str ();\n-\t  };\n+template <typename ManagedTokenSource>\n+void\n+Parser<ManagedTokenSource>::emit_function_qualifier_order_error_msg (\n+  location_t locus, const std::vector<TokenId> &found_order)\n+{\n+  std::vector<TokenId> expected_order\n+    = {IDENTIFIER, CONST, ASYNC, UNSAFE, EXTERN_KW};\n \n-\t  std::vector<TokenId> expected_order\n-\t    = {IDENTIFIER, CONST, ASYNC, UNSAFE, EXTERN_KW};\n+  // we only keep the qualifiers actually used in the offending code\n+  std::vector<TokenId>::iterator token_id = expected_order.begin ();\n+  while (token_id != expected_order.end ())\n+    {\n+      if (std::find (found_order.cbegin (), found_order.cend (), *token_id)\n+\t  == found_order.cend ())\n+\t{\n+\t  token_id = expected_order.erase (token_id);\n+\t}\n+      else\n+\t{\n+\t  ++token_id;\n+\t}\n+    }\n \n-\t  // we only keep the qualifiers actually used in the offending code\n-\t  std::vector<TokenId>::const_iterator token_id\n-\t    = expected_order.cbegin ();\n-\t  while (token_id != expected_order.cend ())\n-\t    {\n-\t      if (std::find (found_order.cbegin (), found_order.cend (),\n-\t\t\t     *token_id)\n-\t\t  == found_order.cend ())\n-\t\t{\n-\t\t  token_id = expected_order.erase (token_id);\n-\t\t}\n-\t      else\n-\t\t{\n-\t\t  ++token_id;\n-\t\t}\n-\t    }\n+  auto qualifiers_to_str = [] (const std::vector<TokenId> &token_ids) {\n+    std::ostringstream ss;\n \n-\t  const std::string found_qualifiers = qualifiers_to_str (found_order);\n-\t  const std::string expected_qualifiers\n-\t    = qualifiers_to_str (expected_order);\n+    for (auto id : token_ids)\n+      {\n+\tif (ss.tellp () != 0)\n+\t  ss << ' ';\n \n-\t  location_t error_locus\n-\t    = make_location (locus, locus, lexer.peek_token ()->get_locus ());\n-\t  Error error (\n-\t    error_locus,\n-\t    \"invalid order of function qualifiers; found %qs, expected %qs\",\n-\t    found_qualifiers.c_str (), expected_qualifiers.c_str ());\n-\t  add_error (std::move (error));\n+\tif (id == IDENTIFIER)\n+\t  ss << Values::WeakKeywords::DEFAULT;\n+\telse\n+\t  ss << token_id_keyword_string (id);\n+      }\n \n-\t  return false;\n-\t}\n+    return ss.str ();\n+  };\n \n-      last_priority = priority;\n-    }\n+  const std::string found_qualifiers = qualifiers_to_str (found_order);\n+  const std::string expected_qualifiers = qualifiers_to_str (expected_order);\n \n-  return true;\n+  location_t error_locus\n+    = make_location (locus, locus, lexer.peek_token ()->get_locus ());\n+  Error error (error_locus,\n+\t       \"invalid order of function qualifiers; found %qs, expected %qs\",\n+\t       found_qualifiers.c_str (), expected_qualifiers.c_str ());\n+  add_error (std::move (error));\n }\n \n // Parses generic (lifetime or type) params inside angle brackets (optional).\n@@ -4479,7 +4535,7 @@ Parser<ManagedTokenSource>::parse_inherent_impl_function_or_method (\n     }\n \n   return std::unique_ptr<AST::Function> (\n-    new AST::Function (std::move (ident), std::move (*qualifiers.value ()),\n+    new AST::Function (std::move (ident), std::move (qualifiers.value ()),\n \t\t       std::move (generic_params), std::move (function_params),\n \t\t       std::move (return_type), std::move (where_clause),\n \t\t       std::move (body), std::move (vis),\n@@ -4709,7 +4765,7 @@ Parser<ManagedTokenSource>::parse_trait_impl_function_or_method (\n     }\n \n   return std::unique_ptr<AST::Function> (\n-    new AST::Function (std::move (ident), std::move (*qualifiers.value ()),\n+    new AST::Function (std::move (ident), std::move (qualifiers.value ()),\n \t\t       std::move (generic_params), std::move (function_params),\n \t\t       std::move (return_type), std::move (where_clause),\n \t\t       std::move (body), std::move (vis),\n@@ -6407,7 +6463,7 @@ Parser<ManagedTokenSource>::parse_bare_function_type (\n     }\n \n   return std::unique_ptr<AST::BareFunctionType> (new AST::BareFunctionType (\n-    std::move (for_lifetimes), std::move (*qualifiers.value ()),\n+    std::move (for_lifetimes), std::move (qualifiers.value ()),\n     std::move (params), is_variadic, std::move (variadic_attrs),\n     std::move (return_type), best_try_locus));\n }\ndiff --git a/gcc/rust/parse/rust-parse.h b/gcc/rust/parse/rust-parse.h\nindex 8df990476..dac39da36 100644\n--- a/gcc/rust/parse/rust-parse.h\n+++ b/gcc/rust/parse/rust-parse.h\n@@ -364,10 +364,20 @@ private:\n   std::unique_ptr<AST::Function> parse_function (AST::Visibility vis,\n \t\t\t\t\t\t AST::AttrVec outer_attrs,\n \t\t\t\t\t\t bool is_external = false);\n-  tl::expected<std::unique_ptr<AST::FunctionQualifiers>, Parse::Error::Node>\n+  tl::expected<AST::FunctionQualifiers, Parse::Error::Node>\n   parse_function_qualifiers ();\n-  bool ensure_function_qualifier_order (location_t locus,\n-\t\t\t\t\tstd::vector<TokenId> found_order);\n+  tl::expected<std::pair<std::vector<TokenId>, std::string>, Parse::Error::Node>\n+  parse_function_qualifiers_raw (location_t locus);\n+  bool\n+  ensure_function_qualifier_order (location_t locus,\n+\t\t\t\t   const std::vector<TokenId> &found_order);\n+  tl::expected<AST::FunctionQualifiers, Parse::Error::Node>\n+  function_qualifiers_from_keywords (location_t locus,\n+\t\t\t\t     std::vector<TokenId> keywords,\n+\t\t\t\t     std::string abi);\n+  void emit_function_qualifier_order_error_msg (\n+    location_t locus, const std::vector<TokenId> &found_order);\n+\n   std::vector<std::unique_ptr<AST::GenericParam>>\n   parse_generic_params_in_angles ();\n   template <typename EndTokenPred>\n",
    "prefixes": [
        "gccrs",
        "COMMIT",
        "5/5"
    ]
}