Open
Conversation
Creates docs/feature-elif-preprocessor.md covering motivation, semantics, spec grammar changes, tooling impact, and compatibility for the #elif preprocessor directive (fslang-suggestions#1370).
Add #elif support to the F# lexer with IfDefElif stack entry tracking first-match-wins semantics. The lexer correctly handles: - Basic #if/#elif/#else/#endif chains - Multiple #elif directives - Nested #elif inside other #if blocks - Error cases (#elif after #else, #elif without #if) - Language version gating (requires --langversion:11.0) Fix IDE state serialization to use 2 bits per ifdef stack entry (00=if, 01=else, 10=elif) so IfDefElif state survives encodeLexCont/decodeLexCont round-trips correctly.
…Skip path Add CheckLanguageFeatureAndRecover for PreprocessorElif in the n > 0 branch of the ifdefSkip rule, matching the existing check in the n = 0 path. Add explanatory comment for discarded boolean in token rule's #elif handler. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Test exercises the ifdefSkip rule's n > 0 branch with --langversion:10.0 to verify the CheckLanguageFeatureAndRecover call added in the nested inactive #elif path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- elifIndentedTest: verifies indented #elif with leading whitespace compiles and runs correctly across all branches - elifMustHaveIdentError: verifies bare #elif without expression produces a compilation error - elifAllFalseNoElseTest: verifies #if/#elif chain where all conditions are false and no #else fallback runs with exit code 0 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- elifMustBeFirstWarning: Tests #elif at non-zero column (after block comment) compiles successfully, exercising the shouldStartLine (FS3882) code path - elifMustHaveIdentError: Tests bare #elif without expression triggers FS3883 diagnostic with proper message verification - elifAllFalseNoElseTest: Tests all-false #if/#elif chain without #else compiles and runs with exit code 0 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- elifMustBeFirstWarning: Tests shouldStartLine diagnostic (FS1163) for directive not at column 0, exercising the same code path as FS3882 - elifMustHaveIdentError: Tests FS3883 for bare #elif without expression - elifAllFalseNoElseTest: Tests all-false #if/#elif chain with no #else Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the #if-based proxy test (FS1163) with a proper #elif test that triggers FS3882. Use #if DEFINED with withDefines to activate the branch, then place #elif after code on the same line to trigger shouldStartLine. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- T4+T6: Tokenizer test verifying #elif state encoding round-trip and inactive code classification across lines - T5: Structure/folding test verifying fold regions for #if/#elif/#else/#endif chains - T7: AST trivia test verifying ConditionalDirectiveTrivia.Elif appears in parsed syntax tree Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Q1: Extract evalAndSaveElif helper in lex.fsl ifdefSkip rule - Q2: Merge Else/Elif arms in ServiceStructure.fs using or-pattern - Q3: Unify SaveIfHash/SaveElifHash via saveConditionalHash in LexerStore.fs - Q4: Replace stackTopRange double evaluation with nested ValueSome/ValueNone match Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- B1: elifTakenThenElseSkipped — verifies #else is skipped when prior #elif was taken - B2: elifSecondElifAfterTakenSkipped — verifies subsequent #elif skipped after taken #elif - B3: elifAfterElseErrorInSkipContext — verifies #elif-after-#else error via ifdefSkip path Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Uses the modern FSharpToken API (not legacy FSharpLineTokenizer) to verify FSharpTokenKind.HashElif is emitted for #elif directives. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove redundant `let m = lexbuf.LexemeRange` rebindings in #else ifdefSkip handler (m already in scope from line 1119) - Inline `let isTrue = evalAndSaveElif()` into `if evalAndSaveElif() then` - Add explanatory comment on IfDefElif arm clarifying why stack is not updated Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Clarify that forward compatibility is limited: #elif in inactive code on older compilers is silently ignored (langversion gate prevents in practice) - Update ParsedInputTrivia doc comment to include #elif Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
|
T-Gro
commented
Feb 18, 2026
| @@ -0,0 +1,144 @@ | |||
| # `#elif` Preprocessor Directive | |||
Member
Author
There was a problem hiding this comment.
This file is more of a reviewer pack, will delete it before merging.
Member
Author
|
/run fantomas |
Contributor
🔧 CLI Command Report
✅ Command succeeded, no changes needed. |
Member
Author
|
/run fantomas |
Contributor
🔧 CLI Command Report
✅ Command succeeded, no changes needed. |
Member
Author
|
/run ilverify |
Contributor
🔧 CLI Command Report
✅ Command succeeded, no changes needed. |
- Move 3882,lexHashElifMustBeFirst and 3883,lexHashElifMustHaveIdent to end of FSComp.txt to maintain ascending error code order (they were placed between unnumbered entries and 1171, breaking the sort). - Add 5 new ServiceLexing StackUnexpected ILVerify entries to both Debug and Release net10.0 baselines for the new #elif lexer paths. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The #elif lexer changes produce 5 new ServiceLexing StackUnexpected IL entries. These need to be added to the netstandard2.0 baselines (Debug + Release). The net10.0 baselines are reverted to their original state as the entries added previously were from a pre-merge CI run with different IL offsets. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
netstandard2.0: Replace original 5 entries with new 5 (bootstrap build shifts original offsets, so only the new #elif entries remain stable). net10.0: Add 5 new entries alongside existing 5 (10 total). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The bootstrap build on CI shifts the original ServiceLexing offsets, so only the new #elif-added entries (0x37,0x40,0x87/0x69,0x90/0x72, 0x99/0x7B) remain stable across all 4 baseline configs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
abonie
reviewed
Feb 20, 2026
abonie
reviewed
Feb 20, 2026
abonie
approved these changes
Feb 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Inspired by and following up from #19045 by @Thorium.
RFC FS-1334 · Suggestion #1370
What
Supports
&&,||,!in conditions, nesting, and arbitrary chaining. Gated behind--langversion:11.0.Design decisions
IfDefElifstack entry (not reusingIfDefElse) — lets the lexer distinguish "a branch was taken" from "we are in #else". Uses 2-bit encoding in the IDE ifdef stack, reducing max nesting from 24 to 12 (plenty).lexHashElifNoMatchingIf,lexHashElifAfterElse,lexHashElifMustBeFirst,lexHashElifMustHaveIdent) instead of reusing #else error messages.ConditionalDirectiveTrivia.Elifcase added to syntax trivia — enables fold regions, colorization, and grayout in VS without any editor-side changes.Future work (other repos)
#elif(parse + format roundtrip)#elifgrayout, folding, and completion work end-to-end