]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
xdrgen: Extend error reporting to AST transformation phase
authorChuck Lever <chuck.lever@oracle.com>
Fri, 26 Dec 2025 15:19:33 +0000 (10:19 -0500)
committerChuck Lever <chuck.lever@oracle.com>
Mon, 26 Jan 2026 15:10:58 +0000 (10:10 -0500)
Commit 277df18d7df9 ("xdrgen: Improve parse error reporting") added
clean, compiler-style error messages for syntax errors detected during
parsing. However, semantic errors discovered during AST transformation
still produce verbose Python stack traces.

When an XDR specification references an undefined type, the transformer
raises a VisitError wrapping a KeyError. Before this change:

  Traceback (most recent call last):
    File ".../lark/visitors.py", line 124, in _call_userfunc
      return f(children)
    ...
  KeyError: 'fsh4_mode'
  ...
  lark.exceptions.VisitError: Error trying to process rule "basic":
  'fsh4_mode'

After this change:

  file.x:156:2: semantic error
  Undefined type 'fsh4_mode'

       fsh4_mode mode;
              ^

The new handle_transform_error() function extracts position information
from the Lark tree node metadata and formats the error consistently with
parse error messages.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
tools/net/sunrpc/xdrgen/subcmds/declarations.py
tools/net/sunrpc/xdrgen/subcmds/definitions.py
tools/net/sunrpc/xdrgen/subcmds/lint.py
tools/net/sunrpc/xdrgen/subcmds/source.py
tools/net/sunrpc/xdrgen/xdr_parse.py

index 2fd5c255a547bd9d75d8dc0c5940be7b4290d37e..97ffb76a02f12515d77bc23109bd162d8e8e3f67 100644 (file)
@@ -8,6 +8,7 @@ import logging
 
 from argparse import Namespace
 from lark import logger
+from lark.exceptions import VisitError
 
 from generators.constant import XdrConstantGenerator
 from generators.enum import XdrEnumGenerator
@@ -24,6 +25,7 @@ from xdr_ast import _XdrConstant, _XdrEnum, _XdrPointer
 from xdr_ast import _XdrTypedef, _XdrStruct, _XdrUnion
 from xdr_parse import xdr_parser, set_xdr_annotate
 from xdr_parse import make_error_handler, XdrParseError
+from xdr_parse import handle_transform_error
 
 logger.setLevel(logging.INFO)
 
@@ -63,7 +65,11 @@ def subcmd(args: Namespace) -> int:
             )
         except XdrParseError:
             return 1
-        ast = transform_parse_tree(parse_tree)
+        try:
+            ast = transform_parse_tree(parse_tree)
+        except VisitError as e:
+            handle_transform_error(e, source, args.filename)
+            return 1
 
         gen = XdrHeaderTopGenerator(args.language, args.peer)
         gen.emit_declaration(args.filename, ast)
index 8ea5c57cc37ab4f15caed85c7902f421f2bf930d..d6c2dcd6f78fef4b27a85de0826b2c04f73e770e 100644 (file)
@@ -8,6 +8,7 @@ import logging
 
 from argparse import Namespace
 from lark import logger
+from lark.exceptions import VisitError
 
 from generators.constant import XdrConstantGenerator
 from generators.enum import XdrEnumGenerator
@@ -24,6 +25,7 @@ from xdr_ast import _RpcProgram, _XdrConstant, _XdrEnum, _XdrPointer
 from xdr_ast import _XdrTypedef, _XdrStruct, _XdrUnion
 from xdr_parse import xdr_parser, set_xdr_annotate
 from xdr_parse import make_error_handler, XdrParseError
+from xdr_parse import handle_transform_error
 
 logger.setLevel(logging.INFO)
 
@@ -82,7 +84,11 @@ def subcmd(args: Namespace) -> int:
             )
         except XdrParseError:
             return 1
-        ast = transform_parse_tree(parse_tree)
+        try:
+            ast = transform_parse_tree(parse_tree)
+        except VisitError as e:
+            handle_transform_error(e, source, args.filename)
+            return 1
 
         gen = XdrHeaderTopGenerator(args.language, args.peer)
         gen.emit_definition(args.filename, ast)
index 2c48fa57c4e5abfbc1a6b4bfde60152e43ac1d5b..e1da49632e626c97341042f540decff3f06e22eb 100644 (file)
@@ -8,8 +8,10 @@ import logging
 
 from argparse import Namespace
 from lark import logger
+from lark.exceptions import VisitError
 
 from xdr_parse import xdr_parser, make_error_handler, XdrParseError
+from xdr_parse import handle_transform_error
 from xdr_ast import transform_parse_tree
 
 logger.setLevel(logging.DEBUG)
@@ -27,6 +29,10 @@ def subcmd(args: Namespace) -> int:
             )
         except XdrParseError:
             return 1
-        transform_parse_tree(parse_tree)
+        try:
+            transform_parse_tree(parse_tree)
+        except VisitError as e:
+            handle_transform_error(e, source, args.filename)
+            return 1
 
     return 0
index bc7d38802df3277357eee6cfb5acf56dcebe28da..08c883f547d7c18844b19dad38346e1c8a3e4eb4 100644 (file)
@@ -8,6 +8,7 @@ import logging
 
 from argparse import Namespace
 from lark import logger
+from lark.exceptions import VisitError
 
 from generators.source_top import XdrSourceTopGenerator
 from generators.enum import XdrEnumGenerator
@@ -23,6 +24,7 @@ from xdr_ast import _XdrStruct, _XdrTypedef, _XdrUnion
 
 from xdr_parse import xdr_parser, set_xdr_annotate
 from xdr_parse import make_error_handler, XdrParseError
+from xdr_parse import handle_transform_error
 
 logger.setLevel(logging.INFO)
 
@@ -105,7 +107,11 @@ def subcmd(args: Namespace) -> int:
             )
         except XdrParseError:
             return 1
-        ast = transform_parse_tree(parse_tree)
+        try:
+            ast = transform_parse_tree(parse_tree)
+        except VisitError as e:
+            handle_transform_error(e, source, args.filename)
+            return 1
         match args.peer:
             case "server":
                 generate_server_source(args.filename, ast, args.language)
index 426513be066c0d43015ffbbbc8dd75ca2729df58..38724ad5aea26c27fe972a119d6d90ae68ee4a34 100644 (file)
@@ -7,7 +7,7 @@ import sys
 from typing import Callable
 
 from lark import Lark
-from lark.exceptions import UnexpectedInput, UnexpectedToken
+from lark.exceptions import UnexpectedInput, UnexpectedToken, VisitError
 
 
 # Set to True to emit annotation comments in generated source
@@ -107,6 +107,44 @@ def make_error_handler(source: str, filename: str) -> Callable[[UnexpectedInput]
     return handle_parse_error
 
 
+def handle_transform_error(e: VisitError, source: str, filename: str) -> None:
+    """Report a transform error with context.
+
+    Args:
+        e: The VisitError from Lark's transformer
+        source: The XDR source text being parsed
+        filename: The name of the file being parsed
+    """
+    lines = source.splitlines()
+
+    # Extract position from the tree node if available
+    line_num = 0
+    column = 0
+    if hasattr(e.obj, "meta") and e.obj.meta:
+        line_num = e.obj.meta.line
+        column = e.obj.meta.column
+
+    line_text = lines[line_num - 1] if 0 < line_num <= len(lines) else ""
+
+    # Build the error message
+    msg_parts = [f"{filename}:{line_num}:{column}: semantic error"]
+
+    # The original exception is typically a KeyError for undefined types
+    if isinstance(e.orig_exc, KeyError):
+        msg_parts.append(f"Undefined type '{e.orig_exc.args[0]}'")
+    else:
+        msg_parts.append(str(e.orig_exc))
+
+    # Show the offending line with a caret pointing to the error
+    if line_text:
+        msg_parts.append("")
+        msg_parts.append(f"    {line_text}")
+        prefix = line_text[: column - 1].expandtabs()
+        msg_parts.append(f"    {' ' * len(prefix)}^")
+
+    sys.stderr.write("\n".join(msg_parts) + "\n")
+
+
 def xdr_parser() -> Lark:
     """Return a Lark parser instance configured with the XDR language grammar"""