Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Compiler/FSComp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1813,4 +1813,5 @@ featureAllowLetOrUseBangTypeAnnotationWithoutParens,"Allow let! and use! type an
3876,lexWarnDirectivesMustMatch,"There is another %s for this warning already in line %d."
3877,lexLineDirectiveMappingIsNotUnique,"The file '%s' was also pointed to in a line directive in '%s'. Proper warn directive application may not be possible."
3878,tcAttributeIsNotValidForUnionCaseWithFields,"This attribute is not valid for use on union cases with fields."
3879,xmlDocNotFirstOnLine,"XML documentation comments should be the first non-whitespace text on a line."
featureReturnFromFinal,"Support for ReturnFromFinal/YieldFromFinal in computation expressions to enable tailcall optimization when available on the builder."
7 changes: 7 additions & 0 deletions src/Compiler/SyntaxTree/LexHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ type LexArgs =
mutable indentationSyntaxStatus: IndentationAwareSyntaxStatus
mutable stringNest: LexerInterpolatedStringNesting
mutable interpolationDelimiterLength: int
/// Tracks the line number of the last non-whitespace token seen
mutable lastNonWhitespaceTokenLine: int
}

/// possible results of lexing a long Unicode escape sequence in a string literal, e.g. "\U0001F47D",
Expand All @@ -85,6 +87,7 @@ let mkLexargs
stringNest = []
pathMap = pathMap
interpolationDelimiterLength = 0
lastNonWhitespaceTokenLine = 0
}

/// Register the lexbuf and call the given function
Expand Down Expand Up @@ -445,9 +448,13 @@ module Keywords =
if IsCompilerGeneratedName s then
warning (Error(FSComp.SR.lexhlpIdentifiersContainingAtSymbolReserved (), lexbuf.LexemeRange))

// Track that we've seen a non-whitespace token on this line
args.lastNonWhitespaceTokenLine <- lexbuf.StartPos.Line
args.resourceManager.InternIdentifierToken s

let KeywordOrIdentifierToken args (lexbuf: Lexbuf) s =
// Track that we've seen a non-whitespace token on this line
args.lastNonWhitespaceTokenLine <- lexbuf.StartPos.Line
match keywordTable.TryGetValue s with
| true, v ->
match v with
Expand Down
4 changes: 3 additions & 1 deletion src/Compiler/SyntaxTree/LexHelpers.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ type LexArgs =
mutable ifdefStack: LexerIfdefStack
mutable indentationSyntaxStatus: IndentationAwareSyntaxStatus
mutable stringNest: LexerInterpolatedStringNesting
mutable interpolationDelimiterLength: int }
mutable interpolationDelimiterLength: int
/// Tracks the line number of the last non-whitespace token seen
mutable lastNonWhitespaceTokenLine: int }

type LongUnicodeLexResult =
| SurrogatePair of uint16 * uint16
Expand Down
10 changes: 10 additions & 0 deletions src/Compiler/lex.fsl
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,15 @@ let tryAppendXmlDoc (buff: (range * StringBuilder) option) (s:string) =
| None -> ()
| Some (_, sb) -> ignore(sb.Append s)

/// Check if XML doc comment is positioned after code on the same line
let checkXmlDocLinePosition (args: LexArgs) (lexbuf: UnicodeLexing.Lexbuf) =
let currentLine = lexbuf.StartPos.Line
// If a non-whitespace token was seen on this line before this /// comment,
// then the /// is incorrectly positioned
if args.lastNonWhitespaceTokenLine = currentLine then
let m = lexbuf.LexemeRange
warning (Error(FSComp.SR.xmlDocNotFirstOnLine(), m))

// Utilities for parsing #if/#else/#endif

let shouldStartLine args lexbuf (m:range) err =
Expand Down Expand Up @@ -740,6 +749,7 @@ rule token (args: LexArgs) (skip: bool) = parse
| "///" op_char*
{ // Match exactly 3 slash, 4+ slash caught by preceding rule
let m = lexbuf.LexemeRange
checkXmlDocLinePosition args lexbuf
let doc = lexemeTrimLeft lexbuf 3
let sb = (new StringBuilder(100)).Append(doc)
if not skip then LINE_COMMENT (LexCont.SingleLineComment(args.ifdefStack, args.stringNest, 1, m))
Expand Down
5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.cs.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.de.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.es.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.fr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.it.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.ja.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.ko.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.pl.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.pt-BR.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.ru.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.tr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.zh-Hans.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compiler/xlf/FSComp.txt.zh-Hant.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

namespace FSharp.Compiler.ComponentTests.Language

open Xunit
open FSharp.Test

module XmlDocCommentPositionTests =

[<Fact>]
let ``XML doc comment after code should warn``() =
FSharp """
let x = 42 /// This should trigger warning
"""
|> compile
|> shouldFail
|> withDiagnostics [
(Warning 3879, Line 2, Col 29, Line 2, Col 62, "XML documentation comments should be the first non-whitespace text on a line.")
]

[<Fact>]
let ``XML doc comment at start of line should not warn``() =
FSharp """
/// This is proper documentation
let x = 42
"""
|> compile
|> shouldSucceed

[<Fact>]
let ``XML doc comment with indentation should not warn``() =
FSharp """
module Test =
/// This is properly indented
let x = 42
"""
|> compile
|> shouldSucceed

[<Fact>]
let ``XML doc comment after let binding should warn``() =
FSharp """
let value = "test" /// Bad position
"""
|> compile
|> shouldFail
|> withDiagnostics [
(Warning 3879, Line 2, Col 29, Line 2, Col 45, "XML documentation comments should be the first non-whitespace text on a line.")
]

[<Fact>]
let ``Regular comment after code should not warn``() =
FSharp """
let x = 42 // This is a regular comment, not XML doc
"""
|> compile
|> shouldSucceed

[<Fact>]
let ``Regular comment with double slash after code should not warn``() =
FSharp """
let value = "test" // Regular comment
let other = value + "more" // Another regular comment
"""
|> compile
|> shouldSucceed

[<Fact>]
let ``Multiple regular comments after code should not warn``() =
FSharp """
module Test =
let x = 1 // comment 1
let y = 2 // comment 2
let z = x + y // result
"""
|> compile
|> shouldSucceed

[<Fact>]
let ``Four slash comment after code should not warn``() =
FSharp """
let x = 42 //// This is a four-slash comment, not XML doc
"""
|> compile
|> shouldSucceed
Loading