diff --git a/src/Compiler/Checking/PostInferenceChecks.fs b/src/Compiler/Checking/PostInferenceChecks.fs index b4eb42970a7..bb833eebabd 100644 --- a/src/Compiler/Checking/PostInferenceChecks.fs +++ b/src/Compiler/Checking/PostInferenceChecks.fs @@ -302,12 +302,24 @@ let BindVal cenv env (v: Val) = //printfn "binding %s..." v.DisplayName let alreadyDone = cenv.boundVals.ContainsKey v.Stamp cenv.boundVals[v.Stamp] <- 1 + + let topLevelBindingHiddenBySignatureFile () = + let parentHasSignatureFile () = + match v.TryDeclaringEntity with + | ParentNone -> false + | Parent p -> + match p.TryDeref with + | ValueNone -> false + | ValueSome e -> e.HasSignatureFile + + v.IsModuleBinding && not v.HasSignatureFile && parentHasSignatureFile () + if not env.external && not alreadyDone && cenv.reportErrors && not v.HasBeenReferenced && - not v.IsCompiledAsTopLevel && - not (v.DisplayName.StartsWithOrdinal("_")) && + (not v.IsCompiledAsTopLevel || topLevelBindingHiddenBySignatureFile ()) && + not (v.DisplayName.StartsWithOrdinal("_")) && not v.IsCompilerGenerated then if v.IsCtorThisVal then diff --git a/src/Compiler/Service/FSharpCheckerResults.fs b/src/Compiler/Service/FSharpCheckerResults.fs index 61ba9c39c63..c66bb3e5142 100644 --- a/src/Compiler/Service/FSharpCheckerResults.fs +++ b/src/Compiler/Service/FSharpCheckerResults.fs @@ -270,9 +270,7 @@ type FSharpSymbolUse(denv: DisplayEnv, symbol: FSharpSymbol, inst: TyparInstanti let fileHasSignatureFile = fileSignatureLocation <> fileDeclarationLocation - let symbolIsNotInSignatureFile = m.SignatureLocation = Some m.DeclarationLocation - - fileHasSignatureFile && symbolIsNotInSignatureFile + fileHasSignatureFile && not m.HasSignatureFile || not m.IsModuleValueOrMember || m.Accessibility.IsPrivate | :? FSharpEntity as m -> m.Accessibility.IsPrivate diff --git a/src/Compiler/Symbols/Symbols.fs b/src/Compiler/Symbols/Symbols.fs index 480c58f3af6..56d19effb2a 100644 --- a/src/Compiler/Symbols/Symbols.fs +++ b/src/Compiler/Symbols/Symbols.fs @@ -1826,6 +1826,14 @@ type FSharpMemberOrFunctionOrValue(cenv, d:FSharpMemberOrValData, item) = | P _ -> true | _ -> false + member x.HasSignatureFile = + match fsharpInfo() with + | None -> false + | Some vref -> + match vref.TryDeref with + | ValueNone -> false + | ValueSome v -> v.HasSignatureFile + member _.IsEvent = match d with | E _ -> true diff --git a/src/Compiler/Symbols/Symbols.fsi b/src/Compiler/Symbols/Symbols.fsi index 8a38e2cb03e..e9ce9d8b11e 100644 --- a/src/Compiler/Symbols/Symbols.fsi +++ b/src/Compiler/Symbols/Symbols.fsi @@ -816,6 +816,9 @@ type FSharpMemberOrFunctionOrValue = /// Indicates if this is a method member member IsMethod: bool + /// Indicates if the value has a signature file counterpart + member HasSignatureFile: bool + /// Indicates if this is a property and there exists an associated getter method member HasGetterMethod: bool diff --git a/src/Compiler/TypedTree/TypedTree.fs b/src/Compiler/TypedTree/TypedTree.fs index dfdc9640a0c..0adaac1f63e 100644 --- a/src/Compiler/TypedTree/TypedTree.fs +++ b/src/Compiler/TypedTree/TypedTree.fs @@ -1276,6 +1276,8 @@ type Entity = /// Indicates if we have pre-determined that a type definition has a self-referential constructor using 'as x' member x.HasSelfReferentialConstructor = x.entity_flags.HasSelfReferentialConstructor + + member x.HasSignatureFile = x.SigRange <> x.DefinitionRange /// Set the custom attributes on an F# type definition. member x.SetAttribs attribs = x.entity_attribs <- attribs @@ -2803,6 +2805,9 @@ type Val = // Indicates if this value was declared to be a type function, e.g. "let f<'a> = typeof<'a>" member x.IsTypeFunction = x.val_flags.IsTypeFunction + member x.HasSignatureFile = + x.SigRange <> x.DefinitionRange + /// Get the inline declaration on the value member x.InlineInfo = x.val_flags.InlineInfo diff --git a/src/Compiler/TypedTree/TypedTree.fsi b/src/Compiler/TypedTree/TypedTree.fsi index b63942d2c8c..621a9da12bd 100644 --- a/src/Compiler/TypedTree/TypedTree.fsi +++ b/src/Compiler/TypedTree/TypedTree.fsi @@ -571,6 +571,9 @@ type Entity = /// Indicates if we have pre-determined that a type definition has a self-referential constructor using 'as x' member HasSelfReferentialConstructor: bool + /// Indicates if the value has a signature file counterpart + member HasSignatureFile: bool + /// Get the Abstract IL scope, nesting type metadata for this /// type definition, assuming it is backed by Abstract IL metadata. member ILTyconInfo: TILObjectReprData @@ -2085,6 +2088,9 @@ type Val = member IsTypeFunction: bool + /// Indicates if the value has a signature file counterpart + member HasSignatureFile: bool + /// The value of a value or member marked with [] member LiteralValue: Const option diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/warnon/warnon.fs b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/warnon/warnon.fs index 91322a0595d..6c23e82e288 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/warnon/warnon.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/warnon/warnon.fs @@ -34,3 +34,111 @@ module warnon = |> withDiagnosticMessageMatches "The value 'n' is unused$" |> ignore + [] + let ``warnon unused public function hidden by signature file`` () = + let signatureFile: SourceCodeFileKind = + SourceCodeFileKind.Create( + "Library.fsi", + """ +module Foo + +val add: a:int -> b:int -> int + """ ) + + let implementationFile = + SourceCodeFileKind.Create( + "Library.fs", + """ +module Foo + +let add a b = a + b +let subtract a b = a - b + """ ) + + fsFromString signatureFile + |> FS + |> withAdditionalSourceFile implementationFile + |> withOptions ["--warnon:FS1182"] + |> asLibrary + |> compile + |> withWarningCode 1182 + |> withDiagnosticMessageMatches "The value 'subtract' is unused$" + |> ignore + + [] + let ``Don't warnon unused public function`` () = + let implementationFile = + SourceCodeFileKind.Create( + "Library.fs", + """ +module Foo + +let add a b = a + b +let subtract a b = a - b + """ ) + + fsFromString implementationFile + |> FS + |> withOptions ["--warnon:FS1182"] + |> asLibrary + |> compile + |> withDiagnostics [] + |> ignore + + [] + let ``Don't warnon unused public function hidden by signature file that starts with an underscore`` () = + let signatureFile: SourceCodeFileKind = + SourceCodeFileKind.Create( + "Library.fsi", + """ +module Foo + +val add: a:int -> b:int -> int + """ ) + + let implementationFile = + SourceCodeFileKind.Create( + "Library.fs", + """ +module Foo + +let add a b = a + b +let _subtract a b = a - b + """ ) + + fsFromString signatureFile + |> FS + |> withAdditionalSourceFile implementationFile + |> withOptions ["--warnon:FS1182"] + |> asLibrary + |> compile + |> withDiagnostics [] + |> ignore + + [] + let ``Type extensions are not included in this warnon check`` () = + let signatureFile: SourceCodeFileKind = + SourceCodeFileKind.Create( + "Library.fsi", + """ +module Foo + """ ) + + let implementationFile = + SourceCodeFileKind.Create( + "Library.fs", + """ +module Foo + +type System.Int32 with + member x.Bar () = x + 1 + """ ) + + fsFromString signatureFile + |> FS + |> withAdditionalSourceFile implementationFile + |> withOptions ["--warnon:FS1182"] + |> asLibrary + |> compile + |> shouldSucceed + |> ignore diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl index a0c59428d2d..a968aad5932 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl @@ -2012,7 +2012,6 @@ FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: FSharp.Compiler.Symbols. FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String ToString() FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String[] DependencyFiles FSharp.Compiler.CodeAnalysis.FSharpCheckProjectResults: System.String[] get_DependencyFiles() -FSharp.Compiler.CodeAnalysis.FSharpChecker FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Create(Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.String,System.DateTime],Microsoft.FSharp.Core.FSharpOption`1[System.Tuple`3[System.Object,System.IntPtr,System.Int32]]]], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean]) FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker Instance FSharp.Compiler.CodeAnalysis.FSharpChecker: FSharp.Compiler.CodeAnalysis.FSharpChecker get_Instance() @@ -4782,6 +4781,7 @@ FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean Equals(System.Obj FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean EventIsStandard FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean HasGetterMethod FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean HasSetterMethod +FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean HasSignatureFile FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsActivePattern FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsBaseValue FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsCompilerGenerated @@ -4814,6 +4814,7 @@ FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsValue FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_EventIsStandard() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_HasGetterMethod() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_HasSetterMethod() +FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_HasSignatureFile() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsActivePattern() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsBaseValue() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsCompilerGenerated() diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index 55bf9244652..a968aad5932 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -4781,6 +4781,7 @@ FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean Equals(System.Obj FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean EventIsStandard FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean HasGetterMethod FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean HasSetterMethod +FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean HasSignatureFile FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsActivePattern FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsBaseValue FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsCompilerGenerated @@ -4813,6 +4814,7 @@ FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean IsValue FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_EventIsStandard() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_HasGetterMethod() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_HasSetterMethod() +FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_HasSignatureFile() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsActivePattern() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsBaseValue() FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue: Boolean get_IsCompilerGenerated()