diff --git a/src/Compiler/Checking/infos.fs b/src/Compiler/Checking/infos.fs
index 81c777c3685..3484ae3acfd 100644
--- a/src/Compiler/Checking/infos.fs
+++ b/src/Compiler/Checking/infos.fs
@@ -1519,7 +1519,7 @@ type ILFieldInfo =
match x with
| ILFieldInfo(tinfo, _) -> tinfo.TypeInstOfRawMetadata
#if !NO_TYPEPROVIDERS
- | ProvidedField _ -> [] /// GENERIC TYPE PROVIDERS
+ | ProvidedField _ -> [] // GENERIC TYPE PROVIDERS
#endif
/// Get the name of the field
diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt
index 512e9b4dca7..16701252d0b 100644
--- a/src/Compiler/FSComp.txt
+++ b/src/Compiler/FSComp.txt
@@ -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."
diff --git a/src/Compiler/SyntaxTree/LexHelpers.fs b/src/Compiler/SyntaxTree/LexHelpers.fs
index a4c222720ac..f3d53faf285 100644
--- a/src/Compiler/SyntaxTree/LexHelpers.fs
+++ b/src/Compiler/SyntaxTree/LexHelpers.fs
@@ -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",
@@ -85,6 +87,7 @@ let mkLexargs
stringNest = []
pathMap = pathMap
interpolationDelimiterLength = 0
+ lastNonWhitespaceTokenLine = 0
}
/// Register the lexbuf and call the given function
@@ -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
diff --git a/src/Compiler/SyntaxTree/LexHelpers.fsi b/src/Compiler/SyntaxTree/LexHelpers.fsi
index 2fe067d244a..6dba76830a0 100644
--- a/src/Compiler/SyntaxTree/LexHelpers.fsi
+++ b/src/Compiler/SyntaxTree/LexHelpers.fsi
@@ -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
diff --git a/src/Compiler/lex.fsl b/src/Compiler/lex.fsl
index 1f905bc049a..a49bfcbf7df 100644
--- a/src/Compiler/lex.fsl
+++ b/src/Compiler/lex.fsl
@@ -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 =
@@ -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))
diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf
index 244be90e142..9ddd2fed1a2 100644
--- a/src/Compiler/xlf/FSComp.txt.cs.xlf
+++ b/src/Compiler/xlf/FSComp.txt.cs.xlf
@@ -2037,6 +2037,11 @@
Tento komentář XML není platný: chybí atribut name pro parametr nebo odkaz na parametr
+
+ XML documentation comments should be the first non-whitespace text on a line.
+ XML documentation comments should be the first non-whitespace text on a line.
+
+ This XML comment is invalid: unresolved cross-reference '{0}'Tento komentář XML není platný: nepřeložený křížový odkaz {0}
diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf
index d1b1782d093..c57c10614f5 100644
--- a/src/Compiler/xlf/FSComp.txt.de.xlf
+++ b/src/Compiler/xlf/FSComp.txt.de.xlf
@@ -2037,6 +2037,11 @@
Dieser XML-Kommentar ist ungültig: Attribut "name" für Parameter oder Parameterverweis fehlt.
+
+ XML documentation comments should be the first non-whitespace text on a line.
+ XML documentation comments should be the first non-whitespace text on a line.
+
+ This XML comment is invalid: unresolved cross-reference '{0}'Dieser XML-Kommentar ist ungültig: nicht aufgelöster Querverweis "{0}".
diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf
index cd311cc7fc5..15b9db05ec1 100644
--- a/src/Compiler/xlf/FSComp.txt.es.xlf
+++ b/src/Compiler/xlf/FSComp.txt.es.xlf
@@ -2037,6 +2037,11 @@
El comentario XML no es válido: falta el atributo "name" para el parámetro o la referencia de parámetro
+
+ XML documentation comments should be the first non-whitespace text on a line.
+ XML documentation comments should be the first non-whitespace text on a line.
+
+ This XML comment is invalid: unresolved cross-reference '{0}'El comentario XML no es válido: la referencia cruzada "{0}" no se ha resuelto
diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf
index e05e9ecdf6c..028a942abd4 100644
--- a/src/Compiler/xlf/FSComp.txt.fr.xlf
+++ b/src/Compiler/xlf/FSComp.txt.fr.xlf
@@ -2037,6 +2037,11 @@
Ce commentaire XML est non valide : attribut 'name' manquant pour le paramètre ou la référence de paramètre
+
+ XML documentation comments should be the first non-whitespace text on a line.
+ XML documentation comments should be the first non-whitespace text on a line.
+
+ This XML comment is invalid: unresolved cross-reference '{0}'Ce commentaire XML est non valide : référence croisée non résolue '{0}'
diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf
index a25fd816046..51c0e773cce 100644
--- a/src/Compiler/xlf/FSComp.txt.it.xlf
+++ b/src/Compiler/xlf/FSComp.txt.it.xlf
@@ -2037,6 +2037,11 @@
Questo commento XML non è valido: manca l'attributo 'name' per il parametro o il riferimento a parametro
+
+ XML documentation comments should be the first non-whitespace text on a line.
+ XML documentation comments should be the first non-whitespace text on a line.
+
+ This XML comment is invalid: unresolved cross-reference '{0}'Questo commento XML non è valido: il riferimento incrociato '{0}' non è stato risolto
diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf
index 87b7d40df1e..23ac619d8eb 100644
--- a/src/Compiler/xlf/FSComp.txt.ja.xlf
+++ b/src/Compiler/xlf/FSComp.txt.ja.xlf
@@ -2037,6 +2037,11 @@
この XML コメントは無効です: パラメーターまたはパラメーター参照に 'name' 属性がありません
+
+ XML documentation comments should be the first non-whitespace text on a line.
+ XML documentation comments should be the first non-whitespace text on a line.
+
+ This XML comment is invalid: unresolved cross-reference '{0}'この XML コメントは無効です: 相互参照 '{0}' が未解決です
diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf
index f2fe6e20f97..2b329be44e3 100644
--- a/src/Compiler/xlf/FSComp.txt.ko.xlf
+++ b/src/Compiler/xlf/FSComp.txt.ko.xlf
@@ -2037,6 +2037,11 @@
이 XML 주석이 잘못됨: 매개 변수 또는 매개 변수 참조에 'name' 특성이 없음
+
+ XML documentation comments should be the first non-whitespace text on a line.
+ XML documentation comments should be the first non-whitespace text on a line.
+
+ This XML comment is invalid: unresolved cross-reference '{0}'이 XML 주석이 잘못됨: 확인되지 않은 상호 참조 '{0}'
diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf
index 23a194ff258..d70411255f7 100644
--- a/src/Compiler/xlf/FSComp.txt.pl.xlf
+++ b/src/Compiler/xlf/FSComp.txt.pl.xlf
@@ -2037,6 +2037,11 @@
Ten komentarz XML jest nieprawidłowy: brak atrybutu „name” dla parametru lub odwołania parametru
+
+ XML documentation comments should be the first non-whitespace text on a line.
+ XML documentation comments should be the first non-whitespace text on a line.
+
+ This XML comment is invalid: unresolved cross-reference '{0}'Ten komentarz XML jest nieprawidłowy: nierozpoznany odsyłacz „{0}”
diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf
index 503fc0f073f..608b56e9d3c 100644
--- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf
+++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf
@@ -2037,6 +2037,11 @@
Este comentário XML é inválido: atributo 'name' ausente para parâmetro ou referência de parâmetro
+
+ XML documentation comments should be the first non-whitespace text on a line.
+ XML documentation comments should be the first non-whitespace text on a line.
+
+ This XML comment is invalid: unresolved cross-reference '{0}'Este comentário XML é inválido: referência cruzada não resolvida '{0}'
diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf
index 7013fb0bc83..1e778b36445 100644
--- a/src/Compiler/xlf/FSComp.txt.ru.xlf
+++ b/src/Compiler/xlf/FSComp.txt.ru.xlf
@@ -2037,6 +2037,11 @@
Недопустимый XML-комментарий: отсутствует атрибут "name" для параметра или ссылки на параметр
+
+ XML documentation comments should be the first non-whitespace text on a line.
+ XML documentation comments should be the first non-whitespace text on a line.
+
+ This XML comment is invalid: unresolved cross-reference '{0}'Недопустимый XML-комментарий: неразрешенная перекрестная ссылка "{0}"
diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf
index 49d2a295b45..1caee47ce9b 100644
--- a/src/Compiler/xlf/FSComp.txt.tr.xlf
+++ b/src/Compiler/xlf/FSComp.txt.tr.xlf
@@ -2037,6 +2037,11 @@
Bu XML açıklaması geçersiz: Parametre veya parametre başvurusu için 'name' özniteliği eksik
+
+ XML documentation comments should be the first non-whitespace text on a line.
+ XML documentation comments should be the first non-whitespace text on a line.
+
+ This XML comment is invalid: unresolved cross-reference '{0}'Bu XML açıklaması geçersiz: '{0}' çapraz başvurusu çözümlenmemiş
diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf
index 3fc65eebc96..62e016388f7 100644
--- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf
+++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf
@@ -2037,6 +2037,11 @@
此 XML 注释无效: 参数或参数引用缺少 "name" 属性
+
+ XML documentation comments should be the first non-whitespace text on a line.
+ XML documentation comments should be the first non-whitespace text on a line.
+
+ This XML comment is invalid: unresolved cross-reference '{0}'此 XML 注释无效: 交叉引用“{0}”无法解析
diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf
index 07fea0efd23..7256c4709a6 100644
--- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf
+++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf
@@ -2037,6 +2037,11 @@
此 XML 註解無效: 參數或參數參考沒有 'name' 屬性
+
+ XML documentation comments should be the first non-whitespace text on a line.
+ XML documentation comments should be the first non-whitespace text on a line.
+
+ This XML comment is invalid: unresolved cross-reference '{0}'此 XML 註解無效: 未解析的交互參照 '{0}'
diff --git a/tests/FSharp.Compiler.ComponentTests/Language/XmlDocCommentPositionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/XmlDocCommentPositionTests.fs
new file mode 100644
index 00000000000..86d8df8c238
--- /dev/null
+++ b/tests/FSharp.Compiler.ComponentTests/Language/XmlDocCommentPositionTests.fs
@@ -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 =
+
+ []
+ 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.")
+ ]
+
+ []
+ let ``XML doc comment at start of line should not warn``() =
+ FSharp """
+/// This is proper documentation
+let x = 42
+"""
+ |> compile
+ |> shouldSucceed
+
+ []
+ let ``XML doc comment with indentation should not warn``() =
+ FSharp """
+module Test =
+ /// This is properly indented
+ let x = 42
+"""
+ |> compile
+ |> shouldSucceed
+
+ []
+ 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.")
+ ]
+
+ []
+ let ``Regular comment after code should not warn``() =
+ FSharp """
+let x = 42 // This is a regular comment, not XML doc
+"""
+ |> compile
+ |> shouldSucceed
+
+ []
+ 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
+
+ []
+ 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
+
+ []
+ let ``Four slash comment after code should not warn``() =
+ FSharp """
+let x = 42 //// This is a four-slash comment, not XML doc
+"""
+ |> compile
+ |> shouldSucceed
\ No newline at end of file