Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Sep 8, 2025

Description

Adds a new compiler warning (FS3879) that detects when triple-slash XML documentation comments (///) are not positioned as the first non-whitespace text on a line. This helps developers identify misplaced XML doc comments that would be ignored or misinterpreted by documentation tools.

Problem

F# developers sometimes write XML documentation comments after code on the same line, which causes the documentation to be lost or misinterpreted:

let value = 42 /// This documentation is ignored
type MyDU = | CaseA of int /// This is also ignored

Solution

The implementation adds:

  1. New warning FS3879: "XML documentation comments should be the first non-whitespace text on a line."

  2. Token position tracking in LexFilter and XmlDocCollector: Tracks the line number of the last non-comment/non-whitespace token using the existing token stream in LexFilter.fs

  3. XmlDocCollector enhancement: Added lastNonCommentTokenLine field to track when non-comment tokens appear

  4. LexerStore methods: Added SetLastNonCommentTokenLine and GetLastNonCommentTokenLine to manage the tracking

  5. Warning check in LexFilter: When a /// LINE_COMMENT token is encountered in rulesForBothSoftWhiteAndHardWhite, checks if it's on the same line as the last non-comment token and emits the warning

Examples

Should trigger warning:

let x = 42                  /// Bad: after code
type Foo() /// Bad: after closing paren
type MyDU = | CaseA of int /// Bad: after DU case

No warning (correct usage):

/// Good: proper documentation
let x = 42

module Test =
    /// Good: properly indented  
    let y = 43
    
type MyDU =
    /// Good: before DU case
    | CaseA of int

Implementation Details

This implementation uses the LexFilter approach as recommended:

  • Leverages existing token stream tracking in runWrappedLexerInConsistentLexbufState
  • Stores the line number of the last non-comment/non-whitespace token in XmlDocCollector after each token is processed
  • Checks LINE_COMMENT tokens with /// prefix in rulesForBothSoftWhiteAndHardWhite
  • Emits warning FS3879 when /// appears on same line as code
  • This approach properly tracks all token types without needing manual updates in the lexer

Additional Fixes

  • Fixed src/Compiler/Checking/infos.fs where /// was incorrectly used as a regular comment (changed to //)
  • Fixed tests/AheadOfTime/Trimming/Program.fs where /// was incorrectly used as a regular comment (changed to //)
  • Suppressed FS3879 warning in AOT trimming test projects (they have TreatWarningsAsErrors enabled but don't require XML documentation)
  • Updated XmlDocTests to expect the new FS3879 warning alongside the existing FS3520 informational message
  • Updated neg45.bsl baseline to include all three FS3879 warnings (lines 89, 97, and 102)
  • Added test for Discriminated Union cases with misplaced XML doc comments

Checklist

  • Test cases added (including DU case test)
  • Performance benchmarks added in case of performance changes
  • Release notes entry updated

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI changed the title [WIP] Warn on triple-slash /// doc comments not being first text on line (lexer approach) Add warning for XML documentation comments not positioned as first non-whitespace on line Sep 8, 2025
Copilot AI requested a review from T-Gro September 8, 2025 12:34
Copy link
Member

@T-Gro T-Gro left a comment

Choose a reason for hiding this comment

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

@copilot :

You are correctly getting a build failure for src/FSharp.Core/Query.fs , Query.fs at line 1331.

This is a valid XML doc comment, but your approach is utter rubbish.
It must check for being the first non-whitespace token irrespective on any magic hausnumero, you cannot just put a clumsy > 16 in there!

Analyze available data in the lexbuf LexBuffer and make it work without hardcoding a magic number.

Have a look at where LexerStore.SaveXmlDocLine is called, this should give you a hint perharps.

…tead of using hardcoded column limit

Co-authored-by: T-Gro <[email protected]>
Copy link
Contributor Author

Copilot AI commented Sep 8, 2025

@copilot :

You are correctly getting a build failure for [src/FSharp.Core/Query.fs](https://github.com/dotnet/fsharp/pull/18891/files#diff-065f1c8dfd42e253c65b952450fb1db41cc7257196045ca5e7befb7e1f...

Fixed the hardcoded column limit approach. Now properly analyzes the actual line content by examining the lexbuf buffer from start of line to the /// token position, checking for any non-whitespace characters. The Query.fs line 1331 issue is resolved - that properly indented XML doc comment is no longer flagged. The remaining build errors are legitimate issues where /// appears after code on the same line. Commit: 21333cf

Copilot AI changed the title Add warning for XML documentation comments not positioned as first non-whitespace on line [WIP] Add warning for XML documentation comments not positioned as first non-whitespace on line Sep 8, 2025
Copilot AI requested a review from T-Gro September 8, 2025 14:56
…orarily pending proper line analysis implementation

Co-authored-by: T-Gro <[email protected]>
@dotnet dotnet deleted a comment from Copilot AI Sep 19, 2025
@dotnet dotnet deleted a comment from github-actions bot Sep 19, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Sep 19, 2025

❗ Release notes required

@copilot,

Caution

No release notes found for the changed paths (see table below).

Please make sure to add an entry with an informative description of the change as well as link to this pull request, issue and language suggestion if applicable. Release notes for this repository are based on Keep A Changelog format.

The following format is recommended for this repository:

* <Informative description>. ([PR #XXXXX](https://github.com/dotnet/fsharp/pull/XXXXX))

See examples in the files, listed in the table below or in th full documentation at https://fsharp.github.io/fsharp-compiler-docs/release-notes/About.html.

If you believe that release notes are not necessary for this PR, please add NO_RELEASE_NOTES label to the pull request.

You can open this PR in browser to add release notes: open in github.dev

Change path Release notes path Description
src/Compiler docs/release-notes/.FSharp.Compiler.Service/10.0.200.md No release notes found or release notes format is not correct

@T-Gro
Copy link
Member

T-Gro commented Sep 19, 2025

@copilot :

Drop your approach, this is not the right level.
Have a look at LexFilter.fs and follow an implementation there where you could make a decision by poping the next token, and therefore comparing a pair of tokens and their ranges (same line) in the case of the second being a triple slash comment

@abonie
Copy link
Member

abonie commented Dec 9, 2025

@copilot

❌ Build / Test Failures

💥 Issues

  • PowerShell exited with code '1'. — Build_And_Test_AOT_Windows compressed_metadata/Build, trim, publish and check the state of the trimmed app. Build_And_Test_AOT_Windows classic_metadata/Build, trim, publish and check the state of the trimmed app.
  • tests\AheadOfTime\Trimming\check.ps1(66,): error : Build failed for SelfContained_Trimming_Test with exit code 1 — Build_And_Test_AOT_Windows compressed_metadata/Build, trim, publish and check the state of the trimmed app. Build_And_Test_AOT_Windows classic_metadata/Build, trim, publish and check the state of the trimmed app.
  • tests\AheadOfTime\Trimming\check.ps1(72,): error : Build failed for FSharpMetadataResource_Trimming_Test with exit code 1 — Build_And_Test_AOT_Windows compressed_metadata/Build, trim, publish and check the state of the trimmed app. Build_And_Test_AOT_Windows classic_metadata/Build, trim, publish and check the state of the trimmed app.
  • tests\AheadOfTime\Trimming\check.ps1(69,): error : Build failed for StaticLinkedFSharpCore_Trimming_Test with exit code 1 — Build_And_Test_AOT_Windows compressed_metadata/Build, trim, publish and check the state of the trimmed app. Build_And_Test_AOT_Windows classic_metadata/Build, trim, publish and check the state of the trimmed app.
  • .packages\microsoft.dotnet.arcade.sdk\11.0.0-beta.25603.2\tools\Publish.proj(428,5): error : (NETCORE_ENGINEERING_TELEMETRY=Publish) ItemsToPush is not specified. — Benchmarks/Smoke test fast benchmarks

🏗️ Jobs

  • (too many failing jobs)

🧪 Tests (0)

Added NoWarn 3879 to the three AOT trimming test projects that have
TreatWarningsAsErrors enabled. These test projects don't require XML
documentation and shouldn't fail builds due to documentation warnings.

Co-authored-by: abonie <[email protected]>
Copy link
Contributor Author

Copilot AI commented Dec 9, 2025

@copilot

❌ Build / Test Failures...

Fixed the AOT trimming test failures by adding <NoWarn>3879</NoWarn> to the three trimming test project files. These projects have TreatWarningsAsErrors enabled but don't need XML documentation, so the warning should be suppressed. Commit: 0c6e957

@edgarfgp
Copy link
Contributor

edgarfgp commented Dec 9, 2025

I think this will address #18843 right ? . Would be good to add tests that include xml docs in DUs.

@abonie
Copy link
Member

abonie commented Dec 9, 2025

I think this will address #18843 right ? . Would be good to add tests that include xml docs in DUs.

Specifically Eugene's suggestion, yes. Which is not DU-specific, but still good idea to test it.

@copilot Please add a test case for when a triple-slash comment appears after a case in definition of a Discriminated Union type.

Added test "Discriminated Union - triple slash after case definition should warn"
to verify that FS3879 warning is correctly emitted when /// XML doc comments
appear after discriminated union case definitions on the same line.

Co-authored-by: abonie <[email protected]>
Copy link
Contributor Author

Copilot AI commented Dec 9, 2025

I think this will address #18843 right ? . Would be good to add tests that include xml docs in DUs.

Specifically Eugene's suggestion, yes. Which is not DU-specific, but still good idea to test it...

Added test case "Discriminated Union - triple slash after case definition should warn" that verifies the warning triggers when /// appears after DU case definitions. Commit: c2112c5

@abonie
Copy link
Member

abonie commented Dec 10, 2025

@copilot

❌ Build / Test Failures

💥 Issues

  • Bash exited with code '1'. — Plain_Build_Linux/Regular rebuild of FSharp.Compiler.Service.sln Plain_Build_MacOS/Regular rebuild of FSharp.Compiler.Service.sln
  • Cmd.exe exited with code '1'. — EndToEndBuildTests/End to end build tests
  • PowerShell exited with code '1'. — Build_And_Test_AOT_Windows classic_metadata/Build, trim, publish and check the state of the trimmed app. Build_And_Test_AOT_Windows compressed_metadata/Build, trim, publish and check the state of the trimmed app.
  • tests/FSharp.Compiler.Service.Tests/XmlDocTests.fs(1632,10): error FS0002: This function takes too many arguments, or is used in a context where a function is not expected — Linux/Build / Test MacOS/Build / Test
  • tests\FSharp.Compiler.Service.Tests\XmlDocTests.fs(1632,10): error FS0002: This function takes too many arguments, or is used in a context where a function is not expected — (occured at many CI jobs)

🏗️ Jobs

  • (too many failing jobs)

🧪 Tests (0)

Copy link
Contributor Author

Copilot AI commented Dec 10, 2025

@copilot

❌ Build / Test Failures...

Fixed the compilation error. The test was using incorrect lambda signature - changed from (fun _ _ -> ()) to (fun _ -> ()) to match the test framework's expected signature. Commit: 2faae14

@abonie
Copy link
Member

abonie commented Dec 10, 2025

@copilot

❌ Build / Test Failures

💥 Issues

  • Bash exited with code '1'. — Linux/Build / Test MacOS/Build / Test
  • Cmd.exe exited with code '1'. — (occured at many CI jobs)
  • (NETCORE_ENGINEERING_TELEMETRY=Test) Failure running tests — (occured at many CI jobs)
  • (NETCORE_ENGINEERING_TELEMETRY=Test) Error running tests (exit code '1'). — Linux/Build / Test MacOS/Build / Test
  • .packages\microsoft.dotnet.arcade.sdk\11.0.0-beta.25603.2\tools\Publish.proj(428,5): error : (NETCORE_ENGINEERING_TELEMETRY=Publish) ItemsToPush is not specified. — Benchmarks/Smoke test fast benchmarks

🏗️ Jobs

  • (too many failing jobs)

🧪 Tests (1)

  • Discriminated Union - triple slash after case definition should warn / FSharp.Compiler.Service.Tests.XmlDocTests.Discriminated Union - triple slash after case definition should warn: Assert.Equal() Failure: Collections differ ⏎ ↓ (pos 1) ⏎ Expected: [Tuple (Warning 3879, Line 5, Col 19, Line 5, Col 22, "XML documentation comments should be the first non"···), Tuple (Warning 3879, Line 6, Col 11, Line 6, Col 14, "XML documentation comments should be the first non"···)] ⏎ Actual: [Tuple (Warning 3879, Line 5, Col 19, Line 5, Col 22, "XML documentation comments should be the first non"···), Tuple (Warning 3879, Line 6, Col 12, Line 6, Col 15, "XML docume...
    Full build

Copy link
Contributor Author

Copilot AI commented Dec 10, 2025

@copilot

❌ Build / Test Failures...

Fixed the column position mismatch in the DU test. The expected column for line 6 | CaseB /// should be Col 12, Col 15, not Col 11, Col 14. Commit: 4d16c5b

@auduchinok
Copy link
Member

New warning FS3879: "XML documentation comments should be the first non-whitespace text on a line."

Why do we need a new warning here? Why not use FS3520?
Can we go more in-line with #11973?

@T-Gro
Copy link
Member

T-Gro commented Dec 31, 2025

The new warning text is in order to be more helpful by being concrete - as in this case we can tell what exactly is wrong.

The current approach taken is rubbish, this must be scrapped.

@T-Gro
Copy link
Member

T-Gro commented Dec 31, 2025

@copilot :

Rewrite Required - The Current Implementation is Fundamentally Broken

The current approach with updateLastTokenPos calls scattered throughout lex.fsl and LexHelpers.fs is not maintainable and incomplete. It only tracks some tokens (), ], |], |}, >], }, some numbers, identifiers/keywords) but misses strings, operators, >, {, (, [, and many others.

Required Changes

Please rewrite the implementation following this approach:

  • Remove all changes from LexHelpers.fs - Remove lastTokenEndLine, lastTokenEndColumn fields from LexArgs
  • Remove all changes from LexHelpers.fsi - Remove the new fields
  • Remove updateLastTokenPos function and all its calls from lex.fsl - This whole approach is wrong

Correct Implementation Location: LexFilter.fs

The token stream already tracks what you need. In LexFilter.fs, the function runWrappedLexerInConsistentLexbufState() already stores lastTokenEnd = state.EndPos before calling the lexer, and saves it in tokenTup. LastTokenPos.

  • Add tracking in XmlDocCollector (in src/Compiler/SyntaxTree/XmlDoc.fs):

    type XmlDocCollector() =
        // ... existing fields ...
        let mutable lastNonCommentTokenLine = 0
        
        member _. SetLastNonCommentTokenLine(line:  int) =
            lastNonCommentTokenLine <- line
        
        member _. LastNonCommentTokenLine = lastNonCommentTokenLine
  • Add corresponding method in LexerStore. fs (in XmlDocStore module):

    let SetLastNonCommentTokenLine (lexbuf:  Lexbuf) (line: int) =
        let collector = getCollector lexbuf
        collector.SetLastNonCommentTokenLine(line)
    
    let GetLastNonCommentTokenLine (lexbuf:  Lexbuf) =
        let collector = getCollector lexbuf
        collector.LastNonCommentTokenLine
  • In LexFilter.fs, update runWrappedLexerInConsistentLexbufState() after getting the token:

    let runWrappedLexerInConsistentLexbufState() =
        let state = if haveLexbufState then savedLexbufState else getLexbufState()
        setLexbufState state
        let lastTokenEnd = state. EndPos
        let token = lexer lexbuf
    
        // Track last non-comment token line for XML doc position checking
        match token with
        | WHITESPACE _ | LINE_COMMENT _ | COMMENT _ -> ()
        | _ -> XmlDocStore.SetLastNonCommentTokenLine(lexbuf, state.EndPos.Line)
    
        XmlDocStore.AddGrabPoint(lexbuf)
        // ... rest unchanged ... 
  • In lex.fsl, in the /// rule, check using the XmlDocStore:

    | "///" op_char*
        { let m = lexbuf.LexemeRange
          // Check if there was a non-comment token on this line before this /// 
          if XmlDocStore.GetLastNonCommentTokenLine(lexbuf) = m.StartLine then
              warning (Error(FSComp.SR.xmlDocNotFirstOnLine(), m))
          let doc = lexemeTrimLeft lexbuf 3
          // ... rest unchanged ... 
        }

Test Cases to Verify (keep these from current PR)

The following test cases should trigger warning FS3879:

let x = 42                  /// Bad:  after code
type Foo() /// Bad: after closing paren  
type MyDU = | CaseA of int /// Bad: after DU case
| CaseB /// Bad: after case name without fields
member x.N = x.M { f1 = 3 }  /// Bad: after expression

The following should NOT trigger warning (legitimate usage):

/// Good: proper documentation at start of line
let x = 42

module Test =
    /// Good: properly indented  
    let y = 43
    
type MyDU =
    /// Good: before DU case
    | CaseA of int

Files to Keep from Current PR

  • Keep FSComp.txt warning message 3879,xmlDocNotFirstOnLine
  • Keep all .xlf translation files
  • Keep release notes entry
  • Keep test updates in XmlDocTests.fs
  • Keep the neg45.fs fixes (changing /// to // for regular comments)
  • Keep infos.fs fix (changing /// to //)
  • Keep tests/AheadOfTime/Trimming/Program.fs fix
  • Keep the <NoWarn>3879</NoWarn> additions to trimming test projects

- Removed token tracking from LexHelpers.fs, LexHelpers.fsi, and lex.fsl
- Added lastNonCommentTokenLine tracking to XmlDocCollector
- Added SetLastNonCommentTokenLine/GetLastNonCommentTokenLine to LexerStore
- Track non-comment token positions in LexFilter.runWrappedLexerInConsistentLexbufState
- Check LINE_COMMENT tokens for /// prefix and warn if on same line as code
- This approach uses existing token stream tracking instead of incomplete lexer modifications

Co-authored-by: T-Gro <[email protected]>
Copy link
Contributor Author

Copilot AI commented Dec 31, 2025

@copilot :

...

Rewrote the implementation using the LexFilter approach as specified. Removed all changes from LexHelpers.fs, LexHelpers.fsi, and lex.fsl. Now tracks token positions in XmlDocCollector via LexFilter.runWrappedLexerInConsistentLexbufState and checks LINE_COMMENT tokens for /// prefix in rulesForBothSoftWhiteAndHardWhite. This uses the existing token stream tracking instead of scattered updateLastTokenPos calls. Commit: 1006511

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

5 participants