You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Buildalyzer.Workspaces returns an empty workspace when MSBuild fails before Csc runs #341
IAnalyzerResult.GetWorkspace() returns an AdhocWorkspace containing the project but with zero documents and zero metadata references, despite the underlying evaluation having succeeded. The caller has no easy way to detect this — Succeeded/OverallSuccess may both be true (the top-level evaluation succeeded) or false (build failed) and the workspace surface looks the same shape either way.
Reproduces deterministically on, e.g., a shallow git clone of any project that uses Nerdbank.GitVersioning: NBGV's GetBuildVersion task throws in BeforeCompile's dependency closure, MSBuild aborts before CoreCompile invokes Csc, and the resulting workspace is empty.
Root cause
EventProcessor captures evaluation-time properties and items on ProjectStarted and stores them via AnalyzerResult.ProcessProject:
Those properties and items end up on IAnalyzerResult.Properties and IAnalyzerResult.Items regardless of whether the build later fails.
But the workspace-building public surface (IAnalyzerResult.SourceFiles, .References, .AnalyzerReferences, .PreprocessorSymbols, .AdditionalFiles) is sourced exclusively from CompilerCommand:
If CoreCompile never reaches the Csc invocation, CompilerCommand stays null, every accessor returns an empty array, and AnalyzerResultExtensions.AddToWorkspace happily produces an empty workspace from those empty arrays.
Why MSBuildWorkspace doesn't hit this
Roslyn's MSBuildWorkspace BuildHost queries the in-process Build engine API directly for workspace state and streams it back, regardless of whether Build() returned true. Buildalyzer is reconstructing the workspace from build-event log output, so anything that gates on a specific later task firing is fragile.
Suggested fix
When CompilerCommand is null, fall back to evaluation-time data on result.Items / result.Properties:
Documents: result.Items["Compile"] (each IProjectItem has the source path as ItemSpec relative to the project directory).
LangVersion: already done via result.GetProperty("LangVersion") in CreateParseOptions (AnalyzerResultExtensions.cs:166).
Metadata references: harder — Csc's reference list is the resolved closure from ResolveAssemblyReferences. The closest evaluation-time approximation is result.Items["Reference"] plus result.Items["ReferencePath"] if the latter ran. A degraded reference set is better than zero (callers can still get source-level semantic models).
Recommended shape: a private BuildFromItems(...) path inside AnalyzerResultExtensions.GetDocuments / AnalyzerResultExtensions.GetMetadataReferences that activates only when the CompilerCommand-backed path returns empty andresult.Items["Compile"] is non-empty. We ship this as an out-of-tree workaround in a playground built around #340 (the "items-fallback" mode) — it recovers ~99% of documents on shallow-clone NBGV failures and is visibly correct when measured against MSBuildWorkspace.
Workaround consumers should know about today
Always check the workspace contents after GetWorkspace():
varworkspace=result.GetWorkspace();varproject=workspace.CurrentSolution.Projects.Single();if(!project.Documents.Any()){// Don't trust this workspace.}
result.Items["Compile"] and result.Properties are still populated and can be used to construct a workspace directly.
Activity
Reconstruct the workspace when MSBuild fails before the compiler runs (…
Give F# a real design-time build
Build the workspace from failed and shared-output results
Carry reference aliases through the failed-build fallback
Recover analyzer config documents when the build fails before the com…