Skip to content

Macro for defining opcode & instruction enums#7573

Merged
youknowone merged 23 commits intoRustPython:mainfrom
ShaharNaveh:bytecode-instr-super-macro
Apr 15, 2026
Merged

Macro for defining opcode & instruction enums#7573
youknowone merged 23 commits intoRustPython:mainfrom
ShaharNaveh:bytecode-instr-super-macro

Conversation

@ShaharNaveh
Copy link
Copy Markdown
Contributor

@ShaharNaveh ShaharNaveh commented Apr 9, 2026

This macro will generate an Instruction and a Opcode enum, all within rust, no external python scripts with custom config in toml or something.

I'd love to hear what you guys think:)

Summary by CodeRabbit

  • Refactor

    • Bytecode instruction types reorganized and generated via a unified macro for consistency and maintainability.
    • Size and conversion behavior preserved to remain backward-compatible; no runtime behavior changes.
  • New Features

    • Public opcode enumeration types are now exposed for direct inspection and conversion.
    • Opcode metadata generation script updated to include explicit CPython names.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 9, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Replaces handwritten Instruction/PseudoInstruction enums and transmute-based conversions with a macro-generated Opcode/PseudoOpcode plus matching Instruction/PseudoInstruction enums, adds Display for opcodes, moves size assertions, updates opcode metadata generation, and re-exports the new opcode types. (≤50 words)

Changes

Cohort / File(s) Summary
Public API exports
crates/compiler-core/src/bytecode.rs
Added Opcode and PseudoOpcode to the public re-exports.
Macro-generated opcodes & instructions
crates/compiler-core/src/bytecode/instruction.rs
Replaced hand-written Instruction/PseudoInstruction and mem::transmute conversions with a define_opcodes! macro that generates Opcode/PseudoOpcode, macro-generated Instruction/PseudoInstruction enums (with #[repr(...)]), TryFrom/From conversions via match, as_instruction()/opcode() mappings, Display impls, removed direct mem::transmute and earlier core::mem import, and moved core::mem::size_of assertions to the file end.
Opcode metadata generator
scripts/generate_opcode_metadata.py
Changed name-casing logic (to_pascal_caseto_snake_case), added cpython_name field to Opcode NamedTuple, altered parsing to extract explicit Rust and CPython names, reworked enum-body extraction to track braces safely, and updated downstream code to use opcode.cpython_name.

Sequence Diagram(s)

(Skipped — changes are internal enum/macro generation without multi-component sequential runtime flow.)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

  • [RFC] Split oparg type from the opcode enum #6746: Related — introduces explicit Opcode/PseudoOpcode enums and match-based opcode↔instruction mappings, aligning with the RFC's separation goal though it doesn't implement AnyOparg/Instruction-struct splitting.

Poem

🐰
I hopped through enums, bytes, and code,
Macros stitched pathways down the road,
Matches chirp where transmutes fell,
Opcodes sparkle — sized and well,
A mellow hop for bytecode's ode! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the primary change: introducing a macro for defining opcode and instruction enums, which aligns with the main refactoring in the PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

}

#[derive(Clone, Copy, Debug)]
#[repr($typ)]
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to remove this #[repr(...)], but when doing so I get the following error:

thread 'main' (3429) panicked at crates/vm/src/frame.rs:3790:33:  index out of bounds: the len is 5 but the index is 22
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Instruction::LoadAttrMethodWithValues => {
let oparg = LoadAttr::from_u32(u32::from(arg));
let cache_base = self.lasti() as usize;
let attr_name = self.code.names[oparg.name_idx() as usize];

@ShaharNaveh ShaharNaveh marked this pull request as ready for review April 9, 2026 20:31
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
crates/compiler-core/src/bytecode/instruction.rs (1)

32-35: Consider deriving PartialEq and Eq for the opcode enum.

The Opcode and PseudoOpcode enums only derive Clone, Copy, and Debug. Adding PartialEq and Eq would enable equality comparisons and pattern matching in match guards, which may be useful for consumers of these types.

💡 Suggested change
-        #[derive(Clone, Copy, Debug)]
+        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
         pub enum $opcode_name {
             $($op_name),*
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/compiler-core/src/bytecode/instruction.rs` around lines 32 - 35, The
opcode enums only derive Clone, Copy, and Debug; add PartialEq and Eq to the
derive list so consumers can compare values and use match guards. Update the
macro-generated enum declarations (the $opcode_name expansion and the concrete
Opcode and PseudoOpcode enums) to include #[derive(Clone, Copy, Debug,
PartialEq, Eq)] so equality comparisons are available for those types.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/compiler-core/src/bytecode/instruction.rs`:
- Around line 1303-1308: The StoreFastMaybeNull instruction currently declares
its oparg var_num as NameIdx but should use the local variable index type;
change the oparg type for StoreFastMaybeNull from NameIdx to oparg::VarNum so it
matches the local-store semantics (same as StoreFast) and uses the correct
symbol names var_num and oparg::VarNum in the instruction definition.

---

Nitpick comments:
In `@crates/compiler-core/src/bytecode/instruction.rs`:
- Around line 32-35: The opcode enums only derive Clone, Copy, and Debug; add
PartialEq and Eq to the derive list so consumers can compare values and use
match guards. Update the macro-generated enum declarations (the $opcode_name
expansion and the concrete Opcode and PseudoOpcode enums) to include
#[derive(Clone, Copy, Debug, PartialEq, Eq)] so equality comparisons are
available for those types.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 490f37ff-9d9d-4813-9ac5-a6ddc00d643f

📥 Commits

Reviewing files that changed from the base of the PR and between d5a90e5 and 3d5295d.

📒 Files selected for processing (2)
  • crates/compiler-core/src/bytecode.rs
  • crates/compiler-core/src/bytecode/instruction.rs

Comment thread crates/compiler-core/src/bytecode/instruction.rs Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
crates/compiler-core/src/bytecode/instruction.rs (1)

32-35: Consider adding PartialEq, Eq, and Hash derives to opcode enums.

The generated opcode enums (Opcode, PseudoOpcode) only derive Clone, Copy, Debug. Adding PartialEq, Eq, and optionally Hash would enable their use in comparisons and as map/set keys, which could be useful for opcode analysis or caching scenarios.

♻️ Suggested derive expansion
-        #[derive(Clone, Copy, Debug)]
+        #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
         pub enum $opcode_name {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/compiler-core/src/bytecode/instruction.rs` around lines 32 - 35, The
generated opcode enums (the template enum named by $opcode_name, e.g., Opcode
and PseudoOpcode) only derive Clone, Copy, Debug; update the derive attribute to
also include PartialEq, Eq, and Hash so these enums can be compared and used as
map/set keys; modify the derive line in the enum generation template (the block
containing #[derive(Clone, Copy, Debug)] and pub enum $opcode_name { ... }) to
add PartialEq, Eq, Hash, and ensure both generated enums are adjusted
accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@crates/compiler-core/src/bytecode/instruction.rs`:
- Around line 32-35: The generated opcode enums (the template enum named by
$opcode_name, e.g., Opcode and PseudoOpcode) only derive Clone, Copy, Debug;
update the derive attribute to also include PartialEq, Eq, and Hash so these
enums can be compared and used as map/set keys; modify the derive line in the
enum generation template (the block containing #[derive(Clone, Copy, Debug)] and
pub enum $opcode_name { ... }) to add PartialEq, Eq, Hash, and ensure both
generated enums are adjusted accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 2966abcb-4020-4bbc-ba78-d346734c5fff

📥 Commits

Reviewing files that changed from the base of the PR and between 3d5295d and 9c3fd3e.

📒 Files selected for processing (1)
  • crates/compiler-core/src/bytecode/instruction.rs

@ShaharNaveh
Copy link
Copy Markdown
Contributor Author

ping @youknowone

@youknowone
Copy link
Copy Markdown
Member

This PR is splitting Instruction and Opcode, right? I wasn't sure what's the benefit based on this changes. Could you tell me more?

@ShaharNaveh
Copy link
Copy Markdown
Contributor Author

This PR is splitting Instruction and Opcode, right? I wasn't sure what's the benefit based on this changes. Could you tell me more?

yes, exactly. If this approach is good in your opinion I'll make the rest of the code to use the Opcode enum

@youknowone
Copy link
Copy Markdown
Member

@ShaharNaveh The change itself is negative by increasing complexity. It will be justfied if the follow-up changes are beneficial enough. Could you tell me how the seperated type makes the codebase better? That will help me to understand this change better.

@ShaharNaveh
Copy link
Copy Markdown
Contributor Author

ShaharNaveh commented Apr 14, 2026

@ShaharNaveh The change itself is negative by increasing complexity. It will be justfied if the follow-up changes are beneficial enough. Could you tell me how the seperated type makes the codebase better? That will help me to understand this change better.

Sure!

The main benefit would be the less code repetition for things like

op = match op {
Instruction::JumpForward { .. } if target_offset <= current_offset => {
Instruction::JumpBackward {
delta: Arg::marker(),
}
}
Instruction::JumpBackward { .. } if target_offset > current_offset => {
Instruction::JumpForward {
delta: Arg::marker(),
}
}
Instruction::JumpBackwardNoInterrupt { .. }
if target_offset > current_offset =>
{
Instruction::JumpForward {
delta: Arg::marker(),
}
}
_ => op,
};

Where we would right it as:

let opcode = op.opcode();
let new_opcode =  match opcode {
  Opcode::JumpForward if target_offset <= current_offset => Opcode::JumpBackward,
  Opcode::JumpBackward if target_offset > current_offset => Opcode::JumpForward,
  Opcode::JumpBackwardNoInterrupt if target_offset > current_offset => Opcode::JumpForward,
  _ => opcode
};

op = new_opcode.into();

And we have lots of places where an enum like:

enum AnyOpcode {
  Real(Opcode),
  Pseudo(PseudoOpcode)
}

Can reduce lots of boilerplate Instruction::Foo { .. } -> Opcode::Foo boilerplate.

examples:
most of _opcode module code.

match instr {
AnyInstruction::Pseudo(PseudoInstruction::Jump { .. })
| AnyInstruction::Real(Instruction::JumpForward { .. })
| AnyInstruction::Real(Instruction::JumpBackward { .. }) => Some(JumpThreadKind::Plain),
AnyInstruction::Pseudo(PseudoInstruction::JumpNoInterrupt { .. })
| AnyInstruction::Real(Instruction::JumpBackwardNoInterrupt { .. }) => {
Some(JumpThreadKind::NoInterrupt)
}
_ => None,
}

Some(match source {
AnyInstruction::Pseudo(_) => PseudoInstruction::Jump {
delta: Arg::marker(),
}
.into(),
AnyInstruction::Real(Instruction::JumpBackwardNoInterrupt { .. }) => {
Instruction::JumpBackward {
delta: Arg::marker(),
}
.into()
}
AnyInstruction::Real(Instruction::JumpForward { .. })
| AnyInstruction::Real(Instruction::JumpBackward { .. }) => source,
_ => return None,
})

matches!(
instr.real(),
Some(
Instruction::PopJumpIfFalse { .. }
| Instruction::PopJumpIfTrue { .. }
| Instruction::PopJumpIfNone { .. }
| Instruction::PopJumpIfNotNone { .. }
)
)


By having a separate Opcode enum, the PR at #7433 would be more reasonable like explained at #7433 (comment)

Copy link
Copy Markdown
Member

@youknowone youknowone left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your patient. I agree about benefits. Let's try the macro don't look like an alien too much. Then it will be perfect

define_opcodes!(
opcode_enum: Opcode,
instruction_enum: Instruction,
typ: u8,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for better text searchability, I prefer to keep macros as much as original syntax.

Suggested change
typ: u8,
define_opcodes!(
#[repr(u8)]
enum Opcode;
enum Instruction;
)

This will help rg 'enum Opcode' can work as normal enum.

If possible, keeping the original enum syntax as much as possible will be even better:

define_opcodes!(
    #[repr(u8)]
    enum Opcode;
    enum Instruction {
        Cache = 0,
        ...,
        BuildInterpolation { format: Arg<u32> } = 45,
        ...,
    }
)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would I handle the display part with this syntax? I'd rather to not use a 3rd party crate like "paste" to do the conversion to: "BUILD_INTERPOLATION" for example. maybe we can drop that and have an impl Display for Opcode manually?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, forgot about it. It doesn't necessarily need to be real code. But making it close enough to real code helps to search and read code. I don't remember details, but macro could handle doc-comment. Then it will be most close to real code. Otherwise adding a string end of the line will be just fine. Let's just try enough to make this easier to read for rust programmers

@ShaharNaveh ShaharNaveh force-pushed the bytecode-instr-super-macro branch from d657b74 to 93c38d8 Compare April 15, 2026 07:58
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
scripts/generate_opcode_metadata.py (2)

29-31: Minor naming consideration: function produces UPPER_SNAKE_CASE.

The function name to_snake_case typically implies lower_case_with_underscores, but this produces UPPER_CASE_WITH_UNDERSCORES. Consider renaming to to_screaming_snake_case or to_upper_snake_case for clarity.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/generate_opcode_metadata.py` around lines 29 - 31, The function
to_snake_case actually returns UPPER_SNAKE_CASE (screaming snake case); rename
it to something clear like to_screaming_snake_case or to_upper_snake_case and
update all call sites accordingly (search for to_snake_case references) and
adjust any docstrings/comments to reflect screaming/upper snake case behavior to
avoid confusion.

52-60: Consider adding defensive checks for regex matches.

Lines 53-54 call .group() and .groups() directly on regex results. If an entry doesn't match the expected pattern (e.g., due to format changes in instruction.rs), this will raise an AttributeError: 'NoneType' object has no attribute 'group', which can be confusing to debug.

💡 Suggested improvement for clearer error messages
             have_oparg = "Arg<" in entry  # Hacky but works
-            rust_name = re.match(r"(\w+)", entry).group(1)
-            id_num, cpython_name = re.search(r'\((\d+),\s*"([^"]+)"\)', entry).groups()
+            name_match = re.match(r"(\w+)", entry)
+            if not name_match:
+                raise ValueError(f"Could not parse Rust name from entry: {entry!r}")
+            rust_name = name_match.group(1)
+            
+            id_match = re.search(r'\((\d+),\s*"([^"]+)"\)', entry)
+            if not id_match:
+                raise ValueError(f"Could not parse (id, cpython_name) from entry: {entry!r}")
+            id_num, cpython_name = id_match.groups()
             yield cls(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/generate_opcode_metadata.py` around lines 52 - 60, Add defensive
checks around the regex matches for rust_name and (id_num, cpython_name) so a
malformed entry does not cause a cryptic AttributeError: verify
re.match(r"(\w+)", entry) and re.search(r'\((\d+),\s*"([^"]+)"\)', entry)
returned non-None before calling .group()/.groups(), and if either is None raise
a clear ValueError that includes the offending entry string (and optional
context like the index or source filename) so callers of the generator (the
class constructor via cls(...), and variables have_oparg / rust_name) get a
descriptive error instead of NoneType attribute errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/generate_opcode_metadata.py`:
- Around line 69-85: The extract_enum_body function currently returns None when
the enum isn't found or when no closing brace is located; change this to raise a
clear exception (e.g., ValueError or RuntimeError) that includes the enum name
so callers (like where the result is joined into a string) never receive None.
Specifically, in extract_enum_body replace the early "return None" when
start_match is falsy with a raised exception specifying the missing enum name,
and after the loop add a raised exception for an unclosed enum block; keep the
return of the inner content when a matching closing brace is found and ensure
the function signature and docstring reflect that it either returns str or
raises.

---

Nitpick comments:
In `@scripts/generate_opcode_metadata.py`:
- Around line 29-31: The function to_snake_case actually returns
UPPER_SNAKE_CASE (screaming snake case); rename it to something clear like
to_screaming_snake_case or to_upper_snake_case and update all call sites
accordingly (search for to_snake_case references) and adjust any
docstrings/comments to reflect screaming/upper snake case behavior to avoid
confusion.
- Around line 52-60: Add defensive checks around the regex matches for rust_name
and (id_num, cpython_name) so a malformed entry does not cause a cryptic
AttributeError: verify re.match(r"(\w+)", entry) and
re.search(r'\((\d+),\s*"([^"]+)"\)', entry) returned non-None before calling
.group()/.groups(), and if either is None raise a clear ValueError that includes
the offending entry string (and optional context like the index or source
filename) so callers of the generator (the class constructor via cls(...), and
variables have_oparg / rust_name) get a descriptive error instead of NoneType
attribute errors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: e118bf35-54e6-417d-9509-47c0fe076d7d

📥 Commits

Reviewing files that changed from the base of the PR and between 048515b and 5b40098.

📒 Files selected for processing (1)
  • scripts/generate_opcode_metadata.py

Comment on lines +69 to +85
def extract_enum_body(text: str, name: str) -> str:
# Find the start of the enum block
start_match = re.search(rf"enum\s+{name}\s*\{{", text)
if not start_match:
return None

return "\n".join(
line.split("//")[0].strip() # Remove any comment. i.e. "foo // some comment"
for line in res.group(1).splitlines()
if not line.strip().startswith("//") # Ignore comment lines
)
# Manually track brace depth from that point
depth = 0
start = start_match.end() - 1 # position of opening '{'
for i, ch in enumerate(text[start:], start):
if ch == "{":
depth += 1
elif ch == "}":
depth -= 1
if depth == 0:
# Return only the inner content (excluding outer braces)
return text[start + 1 : i]
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Function can return None but callers don't handle it.

The function returns None explicitly on line 73 (enum not found) and implicitly if no closing brace is found (loop completes without return). However, the caller at lines 138-141 uses this in "\n".join(...), which will raise a TypeError with an unhelpful message if None is returned.

💡 Suggested improvement: raise explicit error instead of returning None
 def extract_enum_body(text: str, name: str) -> str:
     # Find the start of the enum block
     start_match = re.search(rf"enum\s+{name}\s*\{{", text)
     if not start_match:
-        return None
+        raise ValueError(f"Could not find 'enum {name}' in source file")
 
     # Manually track brace depth from that point
     depth = 0
     start = start_match.end() - 1  # position of opening '{'
     for i, ch in enumerate(text[start:], start):
         if ch == "{":
             depth += 1
         elif ch == "}":
             depth -= 1
             if depth == 0:
                 # Return only the inner content (excluding outer braces)
                 return text[start + 1 : i]
+    
+    raise ValueError(f"Could not find matching closing brace for 'enum {name}'")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/generate_opcode_metadata.py` around lines 69 - 85, The
extract_enum_body function currently returns None when the enum isn't found or
when no closing brace is located; change this to raise a clear exception (e.g.,
ValueError or RuntimeError) that includes the enum name so callers (like where
the result is joined into a string) never receive None. Specifically, in
extract_enum_body replace the early "return None" when start_match is falsy with
a raised exception specifying the missing enum name, and after the loop add a
raised exception for an unclosed enum block; keep the return of the inner
content when a matching closing brace is found and ensure the function signature
and docstring reflect that it either returns str or raises.

Copy link
Copy Markdown
Contributor Author

@ShaharNaveh ShaharNaveh Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's okay, we want to crash in case of returned "None" here

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ShaharNaveh, understood! That's a reasonable approach for a utility script — if the enum isn't found, a crash is the desired behavior and the TypeError from "\n".join(None, ...) will surface it. Thanks for the clarification!


✏️ Learnings added
Learnt from: ShaharNaveh
Repo: RustPython/RustPython PR: 7573
File: scripts/generate_opcode_metadata.py:69-85
Timestamp: 2026-04-15T08:58:35.342Z
Learning: In `scripts/generate_opcode_metadata.py` (RustPython repo), `extract_enum_body` intentionally returns `None` when the enum is not found or the closing brace is missing. The author wants the caller to crash with a `TypeError` in that case, rather than raising an explicit exception. Do not suggest converting `return None` to `raise ValueError(...)` in this function.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@ShaharNaveh ShaharNaveh requested a review from youknowone April 15, 2026 09:32
Copy link
Copy Markdown
Member

@youknowone youknowone left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Looks great! Thank you so much!

@youknowone youknowone merged commit 330aa08 into RustPython:main Apr 15, 2026
37 of 38 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants