diff --git a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md index 965957dbc0c..313a0cf7deb 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -1,5 +1,6 @@ ### Fixed +* Report `FS0037 Duplicate definition of type or module` at type-check time when two sibling modules in a `module rec` / `namespace rec` group share a name, instead of letting the duplicate slip through to IL emit where it surfaced as the cryptic `FS2014: duplicate entry . in type index table`. ([Issue #6694](https://github.com/dotnet/fsharp/issues/6694), [PR #19913](https://github.com/dotnet/fsharp/pull/19913)) * Suppress hover/symbol resolution for wildcard `_` patterns inside `member _.…` bodies that incorrectly showed `val _: T` tooltip. ([PR #19760](https://github.com/dotnet/fsharp/pull/19760)) * Deduplicate format specifier locations in computation expressions so editor tooling no longer reports duplicate entries for the same `%` specifier. ([Issue #16419](https://github.com/dotnet/fsharp/issues/16419), [PR #19791](https://github.com/dotnet/fsharp/pull/19791)) * Reject non-function bindings for single-case and partial active pattern names with FS1209, matching the existing multi-case behavior. ([PR #19763](https://github.com/dotnet/fsharp/pull/19763)) diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index 4a4361e9808..1811bda062d 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -4095,6 +4095,11 @@ module EstablishTypeDefinitionCores = let envTmp, withEnvs = (envInitial, withEntities) ||> MutRecShapes.computeEnvs (fun envAbove (MutRecDefnsPhase2DataForModule (moduleTyAcc, moduleEntity)) -> + // In recursive scopes all siblings build their entities in Phase1A against the same + // envInitial, so duplicate sibling-module names are not yet visible there. Detect + // them here at the publish point, where envAbove's accumulator already contains + // any earlier-published siblings. See https://github.com/dotnet/fsharp/issues/6694. + CheckForDuplicateModule envAbove moduleEntity.DemangledModuleOrNamespaceName moduleEntity.Range PublishModuleDefn cenv envAbove moduleEntity MakeInnerEnvWithAcc true envAbove moduleEntity.Id moduleTyAcc moduleEntity.ModuleOrNamespaceType.ModuleOrNamespaceKind) (fun envAbove _ -> envAbove) diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ModuleTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ModuleTests.fs index 4ebb36eac5c..e5c76ccd542 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ModuleTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ModuleTests.fs @@ -191,4 +191,100 @@ AutoOpen>] L1 = do () |> shouldFail |> withDiagnostics [ Error 10, Line 3, Col 1, Line 3, Col 9, "Unexpected start of structured construct in attribute list" - ] \ No newline at end of file + ] + + [] + let ``Recursive module with duplicate sibling modules emits FS0037`` () = + FSharp """ +module rec A + +module Foobar = let x = 1 +module Foobar = let y = 2 +""" + |> typecheck + |> shouldFail + |> withErrorCode 37 + |> withDiagnosticMessageMatches "Foobar" + |> ignore + + [] + let ``Recursive namespace with duplicate sibling modules emits FS0037`` () = + FSharp """ +namespace rec N + +module Foobar = let x = 1 +module Foobar = let y = 2 +""" + |> typecheck + |> shouldFail + |> withErrorCode 37 + |> withDiagnosticMessageMatches "Foobar" + |> ignore + + [] + let ``Recursive module nested cousins with same name compile`` () = + FSharp """ +module rec Outer + +module A = + module Inner = let x = 1 + +module B = + module Inner = let y = 2 +""" + |> typecheck + |> shouldSucceed + + [] + let ``Recursive module nested siblings with duplicate name emit FS0037`` () = + FSharp """ +module rec Outer + +module Inner = + module Dup = let x = 1 + module Dup = let y = 2 +""" + |> typecheck + |> shouldFail + |> withErrorCode 37 + |> withDiagnosticMessageMatches "Dup" + |> ignore + + [] + let ``Non-recursive module with duplicate sibling modules still emits FS0037`` () = + FSharp """ +module A + +module Foobar = let x = 1 +module Foobar = let y = 2 +""" + |> typecheck + |> shouldFail + |> withErrorCode 37 + |> withDiagnosticMessageMatches "Foobar" + |> ignore + + [] + let ``Single file with two namespace fragments and different modules compiles`` () = + FSharp """ +namespace N + +module M = let x = 1 + +namespace N + +module M2 = let y = 2 +""" + |> typecheck + |> shouldSucceed + + [] + let ``Recursive module siblings differing only in case compile`` () = + FSharp """ +module rec A + +module Foobar = let x = 1 +module FooBar = let y = 2 +""" + |> typecheck + |> shouldSucceed \ No newline at end of file