Fix Ambiguity in Elements using Escaped Primitive Keywords - #813
Fix Ambiguity in Elements using Escaped Primitive Keywords#813InsertCreativityHere wants to merge 5 commits into
Conversation
| } | ||
| } | ||
|
|
||
| impl FromStr for Primitive { |
There was a problem hiding this comment.
Lets you convert a string into a Primitive. Added for convenience.
| @@ -415,10 +415,9 @@ fn construct_type_ref( | |||
| } | |||
|
|
|||
| fn primitive_to_type_ref_definition(parser: &Parser, primitive: Primitive) -> TypeRefDefinition { | |||
There was a problem hiding this comment.
The one place we were looking up primitive types.
Now it uses the dedicated find_primitive_node, instead of the old find_node.
|
|
||
| Node::Module(_) => Err("modules cannot be linked to".to_owned()), | ||
| Node::Parameter(_) => Err("parameters cannot be linked to".to_owned()), // TODO improve for return members. | ||
| Node::Primitive(_) => Err("primitive types cannot be linked to".to_owned()), |
There was a problem hiding this comment.
These are matching against the result of find_node, which now can never return a Primitive. So this branch is dead code.
| LookupError::DoesNotExist { identifier } => format!("no element named '{identifier}' exists in scope"), | ||
| _ => unreachable!("`find_node_with_scope` reported an error other than `DoesNotExist`"), | ||
| .find_node_by_id(&identifier.value, &commentable.parser_scoped_identifier()) | ||
| .map_err(|lookup_error| { |
There was a problem hiding this comment.
This lookup will now fail for either: undefined types, or primitive types.
Good to have dedicated error messages for both cases.
| /// The AST is primarily for centralizing ownership of Slice elements, but also features lookup functions for finding | ||
| /// nodes (see [`find_node`](Ast::find_node) and [`find_node_with_scope`](Ast::find_node_with_scope)) and their | ||
| /// elements (see [`find_symbol_by_id`](Ast::find_symbol_by_id)). | ||
| /// nodes (see [`find_node_by_id`](Ast::find_node_by_id)) and their elements |
There was a problem hiding this comment.
We used to have 2 functions: find_node and find_node_with_scope.
find_node was only called to lookup primitives, so it was renamed find_primitive_node.
find_node_with_scope was renamed to find_node_by_id to be symmetric with find_symbol_by_id.
| Node::Primitive(OwnedPtr::new(Primitive::String)), | ||
| ]; | ||
|
|
||
| let lookup_table = HashMap::from([ |
There was a problem hiding this comment.
We no longer seed the primitive types in the lookup table, since they are never looked up anymore.
This is the core thing that fixes the problem where module \int32 would overwrite this table's entries.
| identifier: identifier.to_owned(), | ||
| }) | ||
| pub fn find_primitive_node(&self, primitive: Primitive) -> &Node { | ||
| self.elements.get(primitive as usize).expect("Missing primitive node!") |
There was a problem hiding this comment.
The order of the primitives is guaranteed. We know their exact indices.
| self.add_element(element) | ||
| } | ||
|
|
||
| fn lookup_node_by_id<'a>(&'a self, identifier: &str) -> Result<&'a Node, LookupError> { |
There was a problem hiding this comment.
Helper function to pull some common logic out of our find functions.
There was a problem hiding this comment.
🟡 Changes recommended
Doc-link primitive detection regresses for globally-scoped links like {@link ::bool}, producing the wrong lint message compared to previous behavior.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR resolves ambiguity between primitive type keywords (e.g., int32) and user-defined modules/types that are keyword-escaped (e.g., module \int32) by removing primitives from the AST name lookup table and introducing dedicated primitive lookup paths.
Changes:
- Removed primitive keywords from AST identifier lookup and added
Ast::find_primitive_nodefor direct primitive access. - Updated type patching and doc-link patching to use the new scoped lookup API (
find_node_by_id) and handle primitives without AST-name lookup. - Added/expanded tests covering modules/elements named after primitive keywords and ensuring primitive-node ordering assumptions remain valid.
File summaries
| File | Description |
|---|---|
| slicec/tests/primitives/mod.rs | Adds a test to ensure find_primitive_node’s primitive-indexing remains consistent with Primitive ordering. |
| slicec/tests/identifier_tests.rs | Adds coverage for escaped primitive-keyword identifiers (modules/types) and correct type resolution. |
| slicec/tests/comment_tests.rs | Adds coverage ensuring doc links prefer user-defined elements over primitives when names collide. |
| slicec/src/patchers/type_ref_patcher.rs | Switches scoped lookups to find_node_by_id to align with new AST lookup behavior. |
| slicec/src/patchers/comment_link_patcher.rs | Updates doc-link resolution for the new lookup API and primitive handling. |
| slicec/src/parsers/slice/grammar.rs | Switches primitive type-ref construction to use find_primitive_node. |
| slicec/src/grammar/elements/primitive.rs | Adds FromStr for Primitive to support primitive detection in patchers. |
| slicec/src/ast/mod.rs | Removes primitives from the lookup table, adds find_primitive_node, and introduces find_node_by_id + internal lookup_node_by_id. |
Review details
- Files reviewed: 8/8 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
pepone
left a comment
There was a problem hiding this comment.
I think this can be simplified:
-
Use a single lookup mechanism. Splitting
find_primitive_nodefromfind_node_by_idgives primitives special lookup semantics. Primitives should participate in the same scoped lookup as other types. -
Don’t use
\to select between a primitive and a user-defined type. Treat primitives as predefined types in the global type namespace and apply normal shadowing. Under the current lexer, a declaration may still need to be written asstruct \string {}, but an unqualifiedstringreference in that scope should resolve to the struct, while::stringshould resolve to the global primitive. -
Return
Optionfrom the low-levelfind. Name lookup itself has only one failure condition: nothing was found. A typed conversion may still returnResultwhen distinguishing a type mismatch is useful, butDoesNotExistis better represented byNone.
This would also give type references and documentation links the same resolution semantics and avoid the enum-index-based primitive lookup introduced by this PR.
This PR fixes #808, which made it possible to 'overwrite' primitive types
It also fixes #815, which let users reference a primitive type as
::int32in doc-comments, instead of treating that as a globally-scoped user-defined element.But it does not use the proposed solution. It turns out there is a much simpler solution thanks to the fact:
"the Slice Lexer immediately patches primitives types".
TypeRefcan be in 2 states, eitherPatchedorUnpatched. For user-defined types, the parser creates themUnpatched, and then after we've parsed everything, we lookup the names and patch all the types. But for primitives, we patch them immediately, since we already know what they are.This means: We never need to lookup a primitive type by name. So, this PR drops the primitives from the lookup table entirely.
Making it absolutely impossible for a conflict to ever happen. Instead, we now have a dedicated function for looking up the primitive types, which bypasses all the lookup machinery.
What's Changed
This PR changes the following behaviors:
\xxxcan no longer be used to reference the primitive typexxx. Now placing a backslash in front of a keyword means the escaped identifier will always be resolved against user-defined types and never treated as a primitive.