Skip to content

Add MSBuildTask0012: diagnose tasks constructed without TaskEnvironment propagation - #14813

Open
ViktorHofer with Copilot wants to merge 5 commits into
mainfrom
copilot/fix-task-environment-propagation
Open

Add MSBuildTask0012: diagnose tasks constructed without TaskEnvironment propagation#14813
ViktorHofer with Copilot wants to merge 5 commits into
mainfrom
copilot/fix-task-environment-propagation

Conversation

Copilot AI commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Context

MSBuild assigns TaskEnvironment only to the task instances it creates itself (TaskExecutionHost.cs:545, guarded by TaskInstance is IMultiThreadableTask). A task that constructs another ITask directly — the arcade ExecWithRetriesExec pattern — leaves the inner task on TaskEnvironment.Fallback, resolving paths and environment variables against the shared node state.

No existing rule catches this: the outer task uses TaskEnvironment correctly throughout its own body and passes the analyzer cleanly, while handing the real work to a task with no environment.

Changes Made

New rule MSBuildTask0012 (Warning)TaskEnvironmentPropagationAnalyzer reports an object creation when:

  • the containing type is multithreadable (IMultiThreadableTask or [MSBuildMultiThreadableTask]) and holds a readable TaskEnvironment member of its own — a task with nothing to propagate is not reported;
  • the created type implements ITask;
  • the created type can actually receive an environment — a publicly settable TaskEnvironment property (as on ToolTask, where it is public virtual) or a constructor parameter — keeping the diagnostic actionable;
  • the environment is not already supplied via constructor argument, object initializer, or a later assignment on the instance.

Suppression via later assignment spans the whole type: candidates are collected in a symbol-start action and reported in the symbol-end action, so a field configured in a helper method is recognized. Members are matched by type rather than by name, so any TaskEnvironment-typed field or property counts as the source.

Code fixTaskEnvironmentPropagationCodeFixProvider adds the missing entry, creating the object initializer when absent, and is skipped in static contexts where no instance member is reachable:

_runningExec = new Exec
{
    BuildEngine = BuildEngine,
    TaskEnvironment = TaskEnvironment,   // added by the fix
    Command = Command,
};

Docs — rule section plus scope/severity/code-fix table rows in src/TaskAnalyzer/README.md, a "Tasks That Construct Other Tasks" note in documentation/specs/multithreading/thread-safe-tasks.md, and a row in AnalyzerReleases.Unshipped.md.

Testing

20 new tests covering the arcade ToolTask shape, constructor injection, cross-method configuration, field initializers, implicit new(), and static contexts; the TaskAnalyzer.Tests suite is at 268 passing.

Verified end-to-end against the real built Microsoft.Build.Framework/Microsoft.Build.Utilities.Core: the rule fires on a reproduction of the arcade pattern, dotnet format analyzers --diagnostics MSBuildTask0012 applies the fix, and the rebuilt project is warning-free.

Notes

src/Tasks built with -p:BuildAnalyzer=true produces zero MSBuildTask0012 occurrences, so the rule was deliberately left out of that project's WarningsNotAsErrors list — if it ever fires there, the build should fail rather than warn.

Known trade-off: a factory-style method returning new SomeTask() is flagged, consistent with the issue's near-zero false-positive expectation. Diagnostics inside static members are reported but offer no fix, since there is no instance environment to reference.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Hello @copilot, I noticed that you’re changing an .swr file or any file under src/Package/MSBuild.VSSetup.. Please make sure to validate this change by an experimental VS insertion. This is accomplished by pushing to an exp/* branch, which requires write permissions to this repo.

Copilot AI and others added 3 commits August 24, 2026 11:47
…ropagation

Co-authored-by: ViktorHofer <7412651+ViktorHofer@users.noreply.github.com>
…eation sites

Co-authored-by: ViktorHofer <7412651+ViktorHofer@users.noreply.github.com>
Co-authored-by: ViktorHofer <7412651+ViktorHofer@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix TaskAnalyzer to propagate TaskEnvironment to inner tasks Add MSBuildTask0012: diagnose tasks constructed without TaskEnvironment propagation Aug 24, 2026
Copilot AI requested a review from ViktorHofer August 24, 2026 11:55
@ViktorHofer
ViktorHofer marked this pull request as ready for review August 24, 2026 12:53
Copilot AI lite review requested due to automatic review settings August 24, 2026 12:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new TaskAnalyzer diagnostic (MSBuildTask0012) to catch multithreadable tasks that construct inner ITask instances without propagating TaskEnvironment, addressing #14792 and preventing inner tasks from silently using TaskEnvironment.Fallback.

Changes:

  • Introduces TaskEnvironmentPropagationAnalyzer (MSBuildTask0012) to detect missing propagation via ctor args, object initializers, or later assignments.
  • Adds TaskEnvironmentPropagationCodeFixProvider to auto-insert TaskEnvironment = <source> into object initializers when an instance source is available.
  • Updates analyzer documentation/spec text and adds new unit tests covering common construction/configuration patterns.
Show a summary per file
File Description
src/TaskAnalyzer/TaskEnvironmentPropagationCodeFixProvider.cs New code fix provider for MSBuildTask0012 that adds a TaskEnvironment initializer entry when possible.
src/TaskAnalyzer/TaskEnvironmentPropagationAnalyzer.cs New analyzer implementing MSBuildTask0012 detection and suppression via later assignment tracking.
src/TaskAnalyzer/README.md Documents MSBuildTask0012 and updates rule/scope/code-fix tables.
src/TaskAnalyzer/DiagnosticIds.cs Adds the MSBuildTask0012 diagnostic ID constant.
src/TaskAnalyzer/DiagnosticDescriptors.cs Adds the MSBuildTask0012 descriptor and includes it in the descriptor list.
src/TaskAnalyzer/AnalyzerReleases.Unshipped.md Records MSBuildTask0012 in the unshipped analyzer releases list.
src/TaskAnalyzer.Tests/TestHelpers.cs Updates framework stubs to model ToolTask as IMultiThreadableTask with TaskEnvironment.
src/TaskAnalyzer.Tests/TaskEnvironmentPropagationCodeFixProviderTests.cs New tests validating the code fix behavior (including static-context no-fix).
src/TaskAnalyzer.Tests/TaskEnvironmentPropagationAnalyzerTests.cs New tests validating detection/suppression scenarios for MSBuildTask0012.
documentation/specs/multithreading/thread-safe-tasks.md Adds guidance on propagating TaskEnvironment when a task constructs other tasks.

Review details

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

  • Files reviewed: 10/10 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread src/TaskAnalyzer/README.md Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TaskAnalyzer: no diagnostic when a task constructs another ITask without propagating TaskEnvironment

3 participants