Skip to content

Support unsafe extern blocks#137

Closed
l2dy wants to merge 10 commits into
masterfrom
feature/unsafe-extern-grammar
Closed

Support unsafe extern blocks#137
l2dy wants to merge 10 commits into
masterfrom
feature/unsafe-extern-grammar

Conversation

@l2dy
Copy link
Copy Markdown
Owner

@l2dy l2dy commented Nov 1, 2025

For testing and posterity. Do not merge.

TODOs:

  1. Follow progress in Extern types v2 rust-lang/rfcs#3396 ("Extern types v2") to decide whether to drop type as an extern block item.
  2. The parser allows safe/unsafe qualifiers on functions and statics at the grammar level (for better error recovery), but these qualifiers are semantically only valid within extern blocks. This is not currently enforced and should be added to annotators like commit 164e1de combined with commit b24e318.

l2dy added 10 commits October 26, 2025 20:29
Add grammar support for Rust 1.82's unsafe extern blocks feature (RFC 3484):

- Add 'safe' keyword to lexer
- Add SAFE token to parser
- Update Function rule to support 'safe?' modifier
- Update Constant rule to support '(safe | unsafe)?' qualifiers

This enables parsing of:
- unsafe extern "C" { ... } blocks
- safe fn function_name() in extern blocks
- unsafe fn function_name() in extern blocks
- safe/unsafe static items in extern blocks

Semantic validation (preventing both safe and unsafe on same item) will
be added in Phase 4 following the plugin's established pattern of
permissive parsing with semantic-level error checking.

Part of unsafe extern blocks migration (Phase 1 of 7).
Related to RFC 3484.
- Bump stub version from 234 to 235
- Add isUnsafe field to RsForeignModStub with serialization
- Add SAFE_MASK to RsFunctionStub for safe keyword tracking
- Add IS_SAFE_MASK and IS_UNSAFE_MASK to RsConstantStub
- Add PSI extensions: RsForeignModItem.isUnsafe, RsFunction.isSafe,
  RsConstant.isSafe/isUnsafe

Part of RFC 3484 unsafe extern blocks implementation.
- Add parser test fixture unsafe_extern_blocks.rs covering RFC 3484 syntax
- Add test method for unsafe extern blocks in RsCompleteParsingTestCase
- Update extern_block.rs comments to reflect edition 2024 validity
- Regenerate extern_block.txt with updated PSI structure
- Add RsForeignModStubTest for isUnsafe flag on extern blocks
- Add RsForeignItemTest for safe/unsafe qualifiers on foreign items

Parser tests: 4/4 passing
Stub tests: 10/11 passing (1 requires Phase 4 semantic analysis)

Part of RFC 3484 unsafe extern blocks implementation.
… (Phase 4)

Updates isActuallyUnsafe to respect safe keyword in extern blocks:
- Safe foreign functions can be called without unsafe blocks
- Safe foreign statics can be accessed without unsafe blocks
- Explicit unsafe modifier always makes items unsafe
- Default (unmarked) items remain unsafe
- Preserves wasm_bindgen and intrinsics special cases

Changes:
- RsFunction.isActuallyUnsafe: Check isSafe first, return false for safe items
- RsUnsafeExpressionAnnotator: Skip safe foreign statics

Tests:
- Add RsForeignFunctionSafetyTest (6 tests for semantic analysis)
- Add 6 annotator tests for safe/unsafe foreign items
- All tests passing (64/64 across 3 test files)

Part of RFC 3484 unsafe extern blocks implementation.
Implement Edition 2024 validation requiring unsafe keyword on extern
blocks and prevent safe mutable statics in foreign modules.

- Add requiresUnsafeKeyword property to detect Edition 2024+
- Add checkMissingUnsafeOnExternBlock in RsErrorAnnotator
- Add checkSafeMutableStatic validation for foreign statics
- Create AddUnsafeToExternBlockFix quick fix
- Add error messages to RsBundle.properties
- Add edition-based tests (10 tests, all passing)
Implements Phase 6 of the unsafe extern blocks migration plan, adding:

- Keyword completion for safe/unsafe in extern blocks
- Added SAFE to RS_KEYWORDS token set for proper formatter spacing

This phase adds developer-facing IDE features that improve the experience
of working with safe/unsafe foreign functions in Edition 2024 code.
Phase 1 implementation incorrectly added 'safe' as a hard keyword in the
lexer, which broke parsing of standard library code that uses 'safe' as a
variable name (e.g., `Ok(safe) => Some(safe)` in intrinsic.rs).

This commit fixes the implementation by making 'safe' a contextual keyword
following the established pattern for async, dyn, union, etc:

- Remove 'safe' from RustLexer.flex (no hard keyword in lexer)
- Change SAFE token to 'safe_kw' in RustParser.bnf (contextual keyword)
- Add safeKeyword() parser utility that remaps IDENTIFIER to SAFE in context
- Add SAFE to RS_CONTEXTUAL_KEYWORDS token set
- Update PSI extensions to detect SAFE via node.findChildByType()

Now 'safe' works as a keyword in extern blocks while remaining usable as a
regular identifier everywhere else. Standard library parsing test now passes.

Note: The parser allows safe/unsafe qualifiers on functions and statics at
the grammar level (for better error recovery), but these qualifiers are
semantically only valid within extern blocks. This is not currently enforced.

Fixes parsing regression introduced in Phase 1 (commit 6a5cff3).
@l2dy l2dy force-pushed the feature/unsafe-extern-grammar branch from 4e9621f to 31cbfec Compare November 2, 2025 01:13
@l2dy l2dy closed this Nov 2, 2025
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.

1 participant