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 c843ddb3df1..7b096b4adbc 100644
--- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md
+++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md
@@ -83,5 +83,6 @@
 
 * Improvements in error and warning messages: new error FS3885 when `let!`/`use!` is the final expression in a computation expression; new warning FS3886 when a list literal contains a single tuple element (likely missing `;` separator); improved wording for FS0003, FS0025, FS0039, FS0072, FS0247, FS0597, FS0670, FS3082, and SRTP operator-not-in-scope hints. ([PR #19398](https://github.com/dotnet/fsharp/pull/19398))
 * Exception field serialization (`GetObjectData` and field-restoring constructor) is now gated behind `langversion:11` (`LanguageFeature.ExceptionFieldSerializationSupport`). With langversion ≤10, exception codegen is unchanged from pre-#19342 behavior. ([PR #19746](https://github.com/dotnet/fsharp/pull/19746))
+* `UnusedOpens.getUnusedOpens` and `UnusedDeclarations.getUnusedDeclarations` now accept an optional `?includeFilesWithErrors: bool` parameter (default `true`). Pass `includeFilesWithErrors = false` to suppress false-positive unused-open/declaration diagnostics on files containing type errors. ([Issue #16415](https://github.com/dotnet/fsharp/issues/16415), [PR #19854](https://github.com/dotnet/fsharp/pull/19854))
 
 ### Breaking Changes
diff --git a/docs/release-notes/.VisualStudio/18.vNext.md b/docs/release-notes/.VisualStudio/18.vNext.md
index a7a62a728f8..289721c958b 100644
--- a/docs/release-notes/.VisualStudio/18.vNext.md
+++ b/docs/release-notes/.VisualStudio/18.vNext.md
@@ -1,5 +1,6 @@
 ### Fixed
 
+* Suppress false-positive unused-open/declaration diagnostics on files that contain type errors. ([Issue #16415](https://github.com/dotnet/fsharp/issues/16415), [PR #19854](https://github.com/dotnet/fsharp/pull/19854))
 * Fixed Rename incorrectly renaming `get` and `set` keywords for properties with explicit accessors. ([Issue #18270](https://github.com/dotnet/fsharp/issues/18270), [PR #19252](https://github.com/dotnet/fsharp/pull/19252))
 * Fixed Find All References crash when F# project contains non-F# files like `.cshtml`. ([Issue #16394](https://github.com/dotnet/fsharp/issues/16394), [PR #19252](https://github.com/dotnet/fsharp/pull/19252))
 * Find All References for external DLL symbols now only searches projects that reference the specific assembly. ([Issue #10227](https://github.com/dotnet/fsharp/issues/10227), [PR #19252](https://github.com/dotnet/fsharp/pull/19252))
diff --git a/src/Compiler/Service/ServiceAnalysis.fs b/src/Compiler/Service/ServiceAnalysis.fs
index 6455d9f0ff3..3ff101d00dc 100644
--- a/src/Compiler/Service/ServiceAnalysis.fs
+++ b/src/Compiler/Service/ServiceAnalysis.fs
@@ -8,12 +8,13 @@ open System.Runtime.CompilerServices
 open Internal.Utilities.Library
 open FSharp.Compiler
 open FSharp.Compiler.CodeAnalysis
+open FSharp.Compiler.Diagnostics
 open FSharp.Compiler.Symbols
 open FSharp.Compiler.Syntax.PrettyNaming
 open FSharp.Compiler.Text
 open FSharp.Compiler.Text.Range
 
-module UnusedOpens =
+module private UnusedOpensInternal =
 
     let symbolHash =
         HashIdentity.FromFunctions (fun (x: FSharpSymbol) -> x.GetEffectivelySameAsHash()) (fun x y -> x.IsEffectivelySameAs(y))
@@ -319,6 +320,24 @@ module UnusedOpens =
                     return! filterOpenStatements symbolUses openStatements
         }
 
+[<AbstractClass; Sealed>]
+type UnusedOpens =
+
+    /// Get all unused open declarations in a file
+    static member getUnusedOpens
+        (checkFileResults: FSharpCheckFileResults, getSourceLineStr: int -> string, ?includeFilesWithErrors: bool)
+        : Async<range list> =
+        let includeFilesWithErrors = defaultArg includeFilesWithErrors true
+
+        if
+            not includeFilesWithErrors
+            && checkFileResults.Diagnostics
+               |> Array.exists (fun d -> d.Severity = FSharpDiagnosticSeverity.Error)
+        then
+            async.Return []
+        else
+            UnusedOpensInternal.getUnusedOpens (checkFileResults, getSourceLineStr)
+
 module SimplifyNames =
     type SimplifiableRange = { Range: range; RelativeName: string }
 
@@ -406,7 +425,7 @@ module SimplifyNames =
             return (result :> seq<_>)
         }
 
-module UnusedDeclarations =
+module private UnusedDeclarationsInternal =
     let isPotentiallyUnusedDeclaration (symbol: FSharpSymbol) : bool =
         match symbol with
 
@@ -470,3 +489,21 @@ module UnusedDeclarations =
             let unusedRanges = getUnusedDeclarationRanges allSymbolUsesInFile isScriptFile
             return unusedRanges
         }
+
+[<AbstractClass; Sealed>]
+type UnusedDeclarations =
+
+    /// Get all unused declarations in a file
+    static member getUnusedDeclarations
+        (checkFileResults: FSharpCheckFileResults, isScriptFile: bool, ?includeFilesWithErrors: bool)
+        : Async<seq<range>> =
+        let includeFilesWithErrors = defaultArg includeFilesWithErrors true
+
+        if
+            not includeFilesWithErrors
+            && checkFileResults.Diagnostics
+               |> Array.exists (fun d -> d.Severity = FSharpDiagnosticSeverity.Error)
+        then
+            async.Return Seq.empty
+        else
+            UnusedDeclarationsInternal.getUnusedDeclarations (checkFileResults, isScriptFile)
diff --git a/src/Compiler/Service/ServiceAnalysis.fsi b/src/Compiler/Service/ServiceAnalysis.fsi
index 672cf088759..20ef67f6aec 100644
--- a/src/Compiler/Service/ServiceAnalysis.fsi
+++ b/src/Compiler/Service/ServiceAnalysis.fsi
@@ -5,11 +5,14 @@ namespace FSharp.Compiler.EditorServices
 open FSharp.Compiler.CodeAnalysis
 open FSharp.Compiler.Text
 
-module public UnusedOpens =
+[<AbstractClass; Sealed>]
+type UnusedOpens =
 
-    /// Get all unused open declarations in a file
-    val getUnusedOpens:
-        checkFileResults: FSharpCheckFileResults * getSourceLineStr: (int -> string) -> Async<range list>
+    /// Get all unused open declarations in a file.
+    /// Set <paramref name="includeFilesWithErrors"/> to <c>false</c> to skip analysis on files with any Error-severity diagnostic.
+    static member getUnusedOpens:
+        checkFileResults: FSharpCheckFileResults * getSourceLineStr: (int -> string) * ?includeFilesWithErrors: bool ->
+            Async<range list>
 
 module public SimplifyNames =
 
@@ -27,7 +30,11 @@ module public SimplifyNames =
     val getSimplifiableNames:
         checkFileResults: FSharpCheckFileResults * getSourceLineStr: (int -> string) -> Async<seq<SimplifiableRange>>
 
-module public UnusedDeclarations =
+[<AbstractClass; Sealed>]
+type UnusedDeclarations =
 
-    /// Get all unused declarations in a file
-    val getUnusedDeclarations: checkFileResults: FSharpCheckFileResults * isScriptFile: bool -> Async<seq<range>>
+    /// Get all unused declarations in a file.
+    /// Set <paramref name="includeFilesWithErrors"/> to <c>false</c> to skip analysis on files with any Error-severity diagnostic.
+    static member getUnusedDeclarations:
+        checkFileResults: FSharpCheckFileResults * isScriptFile: bool * ?includeFilesWithErrors: bool ->
+            Async<seq<range>>
diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl
index 93f733ef373..97603afa77d 100644
--- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl
+++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl
@@ -4784,8 +4784,8 @@ FSharp.Compiler.EditorServices.UnresolvedSymbol: System.String get_FullName()
 FSharp.Compiler.EditorServices.UnresolvedSymbol: System.String[] Namespace
 FSharp.Compiler.EditorServices.UnresolvedSymbol: System.String[] get_Namespace()
 FSharp.Compiler.EditorServices.UnresolvedSymbol: Void .ctor(System.String, System.String, System.String[])
-FSharp.Compiler.EditorServices.UnusedDeclarations: Microsoft.FSharp.Control.FSharpAsync`1[System.Collections.Generic.IEnumerable`1[FSharp.Compiler.Text.Range]] getUnusedDeclarations(FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults, Boolean)
-FSharp.Compiler.EditorServices.UnusedOpens: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Text.Range]] getUnusedOpens(FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults, Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,System.String])
+FSharp.Compiler.EditorServices.UnusedDeclarations: Microsoft.FSharp.Control.FSharpAsync`1[System.Collections.Generic.IEnumerable`1[FSharp.Compiler.Text.Range]] getUnusedDeclarations(FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults, Boolean, Microsoft.FSharp.Core.FSharpOption`1[System.Boolean])
+FSharp.Compiler.EditorServices.UnusedOpens: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.Text.Range]] getUnusedOpens(FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults, Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,System.String], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean])
 FSharp.Compiler.EditorServices.XmlDocComment: Microsoft.FSharp.Core.FSharpOption`1[System.Int32] IsBlank(System.String)
 FSharp.Compiler.EditorServices.XmlDocParser: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.EditorServices.XmlDocable] GetXmlDocables(FSharp.Compiler.Text.ISourceText, FSharp.Compiler.Syntax.ParsedInput)
 FSharp.Compiler.EditorServices.XmlDocable: Boolean Equals(FSharp.Compiler.EditorServices.XmlDocable)
diff --git a/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs b/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs
index 6b4f6d946f6..582667a6c29 100644
--- a/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs
+++ b/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs
@@ -1,4 +1,4 @@
-﻿module FSharp.Compiler.Service.Tests.ProjectAnalysisTests
+module FSharp.Compiler.Service.Tests.ProjectAnalysisTests
 
 open System.Threading.Tasks
 
diff --git a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/CompilerServiceBenchmarks.fs b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/CompilerServiceBenchmarks.fs
index aa7f623cad0..b0f6300dc6b 100644
--- a/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/CompilerServiceBenchmarks.fs
+++ b/tests/benchmarks/FCSBenchmarks/CompilerServiceBenchmarks/CompilerServiceBenchmarks.fs
@@ -259,7 +259,7 @@ type CompilerServiceBenchmarks() =
         | FSharpCheckFileAnswer.Aborted -> failwith "checker aborted"
         | FSharpCheckFileAnswer.Succeeded results ->
             let sourceLines = source.Split ([|"\r\n"; "\n"; "\r"|], StringSplitOptions.None)
-            let decls = UnusedOpens.getUnusedOpens(results, fun lineNum -> sourceLines.[Line.toZ lineNum]) |> Async.RunSynchronously
+            let decls = UnusedOpens.getUnusedOpens(results, (fun lineNum -> sourceLines.[Line.toZ lineNum])) |> Async.RunSynchronously
             ignore decls              
 
     [<Benchmark>]
diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs
index f8de2140da1..1109a840c5d 100644
--- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs
+++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs
@@ -31,7 +31,12 @@ type internal UnusedDeclarationsAnalyzer [<ImportingConstructor>] () =
 
                     let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync(nameof (UnusedDeclarationsAnalyzer))
 
-                    let! unusedRanges = UnusedDeclarations.getUnusedDeclarations (checkResults, (isScriptFile document.FilePath))
+                    let! unusedRanges =
+                        UnusedDeclarations.getUnusedDeclarations (
+                            checkResults,
+                            (isScriptFile document.FilePath),
+                            includeFilesWithErrors = false
+                        )
 
                     let! sourceText = document.GetTextAsync()
 
diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs
index eca26429312..481713b782d 100644
--- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs
+++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs
@@ -30,7 +30,11 @@ type internal UnusedOpensDiagnosticAnalyzer [<ImportingConstructor>] () =
                 let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync(nameof UnusedOpensDiagnosticAnalyzer)
 
                 let! unusedOpens =
-                    UnusedOpens.getUnusedOpens (checkResults, (fun lineNumber -> sourceText.Lines[Line.toZ lineNumber].ToString()))
+                    UnusedOpens.getUnusedOpens (
+                        checkResults,
+                        (fun lineNumber -> sourceText.Lines[Line.toZ lineNumber].ToString()),
+                        includeFilesWithErrors = false
+                    )
 
                 return (ValueSome unusedOpens)
         }
diff --git a/vsintegration/tests/UnitTests/UnusedOpensTests.fs b/vsintegration/tests/UnitTests/UnusedOpensTests.fs
index b77d7e257ef..ee75ec06738 100644
--- a/vsintegration/tests/UnitTests/UnusedOpensTests.fs
+++ b/vsintegration/tests/UnitTests/UnusedOpensTests.fs
@@ -36,7 +36,7 @@ let (=>) (source: string) (expectedRanges: ((*line*)int * ((*start column*)int *
         | FSharpCheckFileAnswer.Aborted -> failwithf "ParseAndCheckFileInProject aborted"
         | FSharpCheckFileAnswer.Succeeded(checkFileResults) -> checkFileResults
 
-    let unusedOpenRanges = UnusedOpens.getUnusedOpens (checkFileResults, fun lineNum -> sourceLines.[Line.toZ lineNum]) |> Async.RunSynchronously
+    let unusedOpenRanges = UnusedOpens.getUnusedOpens (checkFileResults, (fun lineNum -> sourceLines.[Line.toZ lineNum])) |> Async.RunSynchronously
     
     unusedOpenRanges 
     |> List.map (fun x -> x.StartLine, (x.StartColumn, x.EndColumn))
@@ -851,3 +851,248 @@ printfn "%A" MyModule.Thingy.Thing
     """
     => [6, (10, 25)]
 
+[<Fact>]
+let ``unused opens analysis - no false positive when open is used after a type error``() =
+    """
+module M
+open System.Collections.Generic
+let d = Dictionary<string, int>()
+let _ = d.Add("x", 1 + "not an int")
+let _ = d.["x"]
+"""
+    => []
+
+[<Fact>]
+let ``unused opens analysis - multiple opens, only post-error use, none flagged when file has errors``() =
+    """
+module M
+open System
+open System.Collections.Generic
+let _ = DateTime.Now
+let d = Dictionary<string, int>()
+let _ = d.Add("x", 1 + "not an int")
+let _ = d.["x"]
+"""
+    => []
+
+[<Fact>]
+let ``unused opens analysis - open used in function body, type error in different function``() =
+    """
+module M
+open System.Collections.Generic
+let f () = 1 + "not an int"
+let g () = Dictionary<string, int>()
+"""
+    => []
+
+[<Fact>]
+let ``unused opens analysis - open used only via type annotation after error``() =
+    """
+module M
+open System.Collections.Generic
+let _ = 1 + "not an int"
+let f (x: Dictionary<string, int>) = x
+"""
+    => []
+
+[<Fact>]
+let ``unused opens analysis - nested module open with error after usage``() =
+    """
+module M
+open System.Collections.Generic
+let d = Dictionary<string, int>()
+let _ = d.["x"]
+let _ = 1 + "not an int"
+"""
+    => []
+
+[<Fact>]
+let ``unused opens analysis - file with no errors still reports genuinely unused opens``() =
+    """
+module M
+open System
+open System.IO
+let _ = DateTime.Now
+"""
+    => [ 4, (5, 14) ]
+
+[<Fact>]
+let ``unused opens analysis - file with no errors and used opens reports nothing``() =
+    """
+module M
+open System
+let _ = DateTime.Now
+"""
+    => []
+
+[<Fact>]
+let ``unused opens analysis - warnings do not suppress analysis``() =
+    """
+module M
+open System
+open System.IO
+let f (x: int option) = match x with Some y -> y
+let _ = DateTime.Now
+"""
+    => [ 4, (5, 14) ]
+
+[<Fact>]
+let ``unused opens analysis - all opens used before error, none flagged``() =
+    """
+module M
+open System
+open System.Collections.Generic
+let d = Dictionary<string, int>()
+let _ = DateTime.Now
+let _ = d.Add("x", 1 + "not an int")
+"""
+    => []
+
+[<Fact>]
+let ``unused opens analysis - error on first line, no opens flagged``() =
+    """
+module M
+open System
+let _ = 1 + "not an int"
+let _ = DateTime.Now
+"""
+    => []
+
+[<Fact>]
+let ``unused opens analysis - multiple scattered type errors suppresses analysis``() =
+    """
+module M
+open System.Collections.Generic
+let _ = 1 + "not an int"
+let d = Dictionary<string, int>()
+let _ = "x" + 1
+let _ = d.["x"]
+"""
+    => []
+
+// This test exercises the Error-diagnostic guard in UnusedOpens.getUnusedOpens with includeFilesWithErrors = false.
+// Without the guard, the snippet below produced [ 10, (5, 11) ] because the
+// `'open' must come first in rec` parse error left symbol uses incomplete and
+// the analysis flagged the open as unused. With includeFilesWithErrors = false, the file's Error
+// diagnostic suppresses the analysis and the range list is empty.
+[<Fact>]
+let ``unused opens analysis - parse error in rec module suppresses analysis when includeFilesWithErrors=false``() =
+    let source = """
+module rec TopModule
+
+module Nested =
+    let x = 1
+    let f x = x
+    type T() = class end
+    type R = { F: int }
+
+open Nested
+"""
+    let sourceLines = source.Split ([|"\r\n"; "\n"; "\r"|], System.StringSplitOptions.None)
+
+    let _, checkFileAnswer =
+        checker.ParseAndCheckFileInProject(filePath, 0, FSharp.Compiler.Text.SourceText.ofString source, projectOptions)
+        |> Async.RunSynchronously
+
+    let checkFileResults =
+        match checkFileAnswer with
+        | FSharpCheckFileAnswer.Aborted -> failwithf "ParseAndCheckFileInProject aborted"
+        | FSharpCheckFileAnswer.Succeeded(r) -> r
+
+    let unusedOpenRanges =
+        UnusedOpens.getUnusedOpens (checkFileResults, (fun lineNum -> sourceLines.[Line.toZ lineNum]), includeFilesWithErrors = false)
+        |> Async.RunSynchronously
+
+    Assert.Equal<(int * (int * int)) list>([], unusedOpenRanges |> List.map (fun x -> x.StartLine, (x.StartColumn, x.EndColumn)))
+
+// Verifies UnusedDeclarations.getUnusedDeclarations returns an empty seq<range>
+// when the file has any Error-severity diagnostic and includeFilesWithErrors = false.
+// The let-binding `unused` would normally be flagged as an unused declaration,
+// but the type error combined with includeFilesWithErrors = false must suppress the analysis.
+[<Fact>]
+let ``unused declarations analysis - file with type error returns empty when includeFilesWithErrors=false``() =
+    let source = """
+module M
+let unused = 42
+let _ = 1 + "not an int"
+"""
+    let _, checkFileAnswer =
+        checker.ParseAndCheckFileInProject(filePath, 0, FSharp.Compiler.Text.SourceText.ofString source, projectOptions)
+        |> Async.RunSynchronously
+
+    let checkFileResults =
+        match checkFileAnswer with
+        | FSharpCheckFileAnswer.Aborted -> failwithf "ParseAndCheckFileInProject aborted"
+        | FSharpCheckFileAnswer.Succeeded(r) -> r
+
+    let hasError =
+        checkFileResults.Diagnostics
+        |> Array.exists (fun d -> d.Severity = FSharp.Compiler.Diagnostics.FSharpDiagnosticSeverity.Error)
+    Assert.True(hasError, "expected source to contain at least one Error diagnostic")
+
+    let unused =
+        UnusedDeclarations.getUnusedDeclarations(checkFileResults, false, includeFilesWithErrors = false)
+        |> Async.RunSynchronously
+        |> Seq.toList
+    Assert.Equal<range list>([], unused)
+
+// Verifies UnusedDeclarations.getUnusedDeclarations still reports genuinely
+// unused declarations on a clean file (no Error diagnostics), so the guard
+// doesn't silently disable the analysis everywhere.
+[<Fact>]
+let ``unused declarations analysis - clean file still reports unused``() =
+    let source = """
+module M
+let private unused = 42
+let _ = 1
+"""
+    let _, checkFileAnswer =
+        checker.ParseAndCheckFileInProject(filePath, 0, FSharp.Compiler.Text.SourceText.ofString source, projectOptions)
+        |> Async.RunSynchronously
+
+    let checkFileResults =
+        match checkFileAnswer with
+        | FSharpCheckFileAnswer.Aborted -> failwithf "ParseAndCheckFileInProject aborted"
+        | FSharpCheckFileAnswer.Succeeded(r) -> r
+
+    let hasError =
+        checkFileResults.Diagnostics
+        |> Array.exists (fun d -> d.Severity = FSharp.Compiler.Diagnostics.FSharpDiagnosticSeverity.Error)
+    Assert.False(hasError, "expected clean source to have no Error diagnostics")
+
+    let unused =
+        UnusedDeclarations.getUnusedDeclarations(checkFileResults, false, includeFilesWithErrors = false)
+        |> Async.RunSynchronously
+        |> Seq.toList
+    Assert.NotEmpty(unused)
+
+// Verifies the default (includeFilesWithErrors = true) does NOT suppress analysis on errored files,
+// preserving historical FCS behavior.
+[<Fact>]
+let ``unused declarations analysis - default (includeFilesWithErrors=true) does not suppress on errors``() =
+    let source = """
+module M
+let private unused = 42
+let _ = 1 + "not an int"
+"""
+    let _, checkFileAnswer =
+        checker.ParseAndCheckFileInProject(filePath, 0, FSharp.Compiler.Text.SourceText.ofString source, projectOptions)
+        |> Async.RunSynchronously
+
+    let checkFileResults =
+        match checkFileAnswer with
+        | FSharpCheckFileAnswer.Aborted -> failwithf "ParseAndCheckFileInProject aborted"
+        | FSharpCheckFileAnswer.Succeeded(r) -> r
+
+    let hasError =
+        checkFileResults.Diagnostics
+        |> Array.exists (fun d -> d.Severity = FSharp.Compiler.Diagnostics.FSharpDiagnosticSeverity.Error)
+    Assert.True(hasError, "expected source to contain at least one Error diagnostic")
+
+    // With default (includeFilesWithErrors = true), unused declarations are still reported even with errors
+    let unused =
+        UnusedDeclarations.getUnusedDeclarations(checkFileResults, false, includeFilesWithErrors = true)
+        |> Async.RunSynchronously
+        |> Seq.toList
+    Assert.NotEmpty(unused)
+
