Skip to content

Add analyzer for TaskEnvironment constructor injection#14401

Merged
baronfel merged 3 commits into
dotnet:mainfrom
jankratochvilcz:jankratochvilcz-taskenvironment-injection-analyzer
Jul 20, 2026
Merged

Add analyzer for TaskEnvironment constructor injection#14401
baronfel merged 3 commits into
dotnet:mainfrom
jankratochvilcz:jankratochvilcz-taskenvironment-injection-analyzer

Conversation

@jankratochvilcz

Copy link
Copy Markdown
Contributor

Summary

  • add MSBuildTask0011 at Info severity for concrete IMultiThreadableTask implementations that do not expose a public single-TaskEnvironment constructor
  • cover valid, invalid, abstract, and inherited-constructor cases
  • document the recommended constructor-injection pattern in the analyzer README and thread-safe task specification

Replaces #14400, which GitHub automatically closed when its stacked base branch was deleted after #14315 merged. The branch is now rebased directly onto main. The rule moved from the originally proposed ID 0010 to 0011 because main assigned MSBuildTask0010 in #13974.

Compatibility

The task-authoring analyzer is currently non-shipping/opt-in, and this diagnostic defaults to Info severity, so it does not break builds using warnings-as-errors. No ChangeWave is needed.

Performance

Measured with the BenchmarkDotNet harness from #14387 in a disposable integration checkout, using the default job on an Apple M4 Max with .NET 10.0.8 / SDK 10.0.300. MSBuildTask0009 is the closest existing symbol-action analyzer.

Diagnostics MSBuildTask0009 MSBuildTask0011 0009 allocated 0011 allocated
1 358.4 us 357.9 us 352.02 KB 352.26 KB
10 563.5 us 588.8 us 693.73 KB 683.04 KB
100 2.731 ms 2.835 ms 3,570.28 KB 3,584.23 KB

The initial no-reference path measured 48.08 us / 32.41 KB. Short-circuiting after the mandatory IMultiThreadableTask lookup reduced that to 35.52 us / 30.56 KB, close to MSBuildTask0009 at 31.46 us / 30.56 KB. Diagnostic time, allocations, and 1/10/100 scaling are comparable, so no further complexity is justified.

Validation

Fixes #14378

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 175855cd-9522-42fd-ad8a-928f21c9e4f5
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 175855cd-9522-42fd-ad8a-928f21c9e4f5
Copilot AI review requested due to automatic review settings July 16, 2026 20:18
@jankratochvilcz
jankratochvilcz requested a review from baronfel July 16, 2026 20:18

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

This pull request adds a new Roslyn analyzer rule to the MSBuild task-authoring analyzer package to encourage constructor-based TaskEnvironment injection for concrete IMultiThreadableTask implementations, aligning author guidance with the engine’s newer constructor-injection capability.

Changes:

  • Introduces MSBuildTask0011 (Info) to flag concrete IMultiThreadableTask classes missing a public single-TaskEnvironment constructor.
  • Adds analyzer unit tests covering valid/invalid/abstract/inherited-constructor scenarios.
  • Updates analyzer documentation and the thread-safe tasks specification to document the recommended pattern and the new rule.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/TaskAnalyzer/TaskEnvironmentConstructorInjectionAnalyzer.cs Implements the new MSBuildTask0011 analyzer logic.
src/TaskAnalyzer/README.md Documents the new rule, rationale, and scope/severity guidance.
src/TaskAnalyzer/DiagnosticIds.cs Adds the MSBuildTask0011 diagnostic ID constant.
src/TaskAnalyzer/DiagnosticDescriptors.cs Adds the MSBuildTask0011 descriptor and registers it in the descriptor set.
src/TaskAnalyzer/AnalyzerReleases.Unshipped.md Records the new analyzer rule in the unshipped release notes.
src/TaskAnalyzer.Tests/TaskEnvironmentConstructorInjectionAnalyzerTests.cs Adds unit tests validating analyzer behavior across key cases.
documentation/specs/multithreading/thread-safe-tasks.md Updates the spec to mention the analyzer recommendation and constructor-injection guidance.

Comment thread src/TaskAnalyzer/TaskEnvironmentConstructorInjectionAnalyzer.cs Outdated

@github-actions github-actions Bot 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.

Review Summary — MSBuildTask0011 TaskEnvironmentConstructorInjectionAnalyzer

Overall: Looks good. Clean, well-scoped analyzer with solid test coverage.

Findings

Minor (non-blocking):

  1. SupportedDiagnostics allocation (Performance): The => expression body allocates on every call. Use { get; } = pattern like PreferTypedParameterAnalyzer. Pre-existing inconsistency with TransitiveCallChainAnalyzer.

  2. Potential false positive: If a concrete IMultiThreadableTask does NOT have a TaskEnvironment property at all (e.g. a task that delegates environment usage to another object), the diagnostic still fires. This seems acceptable for an Info-severity suggestion but could be confusing. Consider adding a test for this scenario.

  3. No code fix: Unlike MSBuildTask0008, there is no code fix provider. Fine for initial PR but worth noting for follow-up.

Correctness ✅

  • Analyzer logic is correct: checks concrete, non-abstract classes implementing IMultiThreadableTask, looks for public single-TaskEnvironment-parameter constructors on the declaring type only.
  • The inherited-constructor test correctly validates that base class constructors do not satisfy the check (the engine needs a public ctor on the concrete type).
  • Well-known type resolution with early-return is correct.
  • Thread safety: EnableConcurrentExecution() is called, no shared mutable state.

Test Coverage ✅

  • Positive case, negative cases (abstract, regular task, has ctor), edge cases (private ctor, multi-param ctor, inherited ctor, inherited interface).
  • Missing: task without TaskEnvironment property at all (minor).

Documentation ✅

  • README, spec, and AnalyzerReleases.Unshipped.md all updated consistently.

Breaking Changes: None ✅

  • Info severity does not trigger WarnAsError. No ChangeWave needed for new analyzer diagnostics.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • awmgmcpg

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

Generated by Expert Code Review (on open) for #14401 · 64.1 AIC · ⌖ 5.16 AIC · ⊞ 4.8K

Comment thread src/TaskAnalyzer/TaskEnvironmentConstructorInjectionAnalyzer.cs
@jankratochvilcz
jankratochvilcz requested a deployment to copilot-pat-pool July 16, 2026 20:27 — with GitHub Actions Abandoned

@baronfel baronfel left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks straightforward to me! I do agree with Copilot about caching the SupportedDiagnostics member though.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 175855cd-9522-42fd-ad8a-928f21c9e4f5
@baronfel

Copy link
Copy Markdown
Member

@jankratochvilcz I think the one failing test is a flaky test not related to this change. Re-approved!

@baronfel
baronfel enabled auto-merge (squash) July 20, 2026 13:49
@baronfel
baronfel merged commit eae5402 into dotnet:main Jul 20, 2026
10 checks passed
AlesProkop pushed a commit that referenced this pull request Jul 24, 2026
Quarantines 2 intermittently failing tests detected across multiple
independent CI sources.

---

## `NuGetStaticGraphRestore_InheritsCoordinatorGrant_DoesNotDeadlock`

**Flaky-test key** (automated de-duplication — do not edit):
```text
flaky-test-id: Microsoft.Build.Coordinator.UnitTests.CoordinatorIntegration_Tests.NuGetStaticGraphRestore_InheritsCoordinatorGrant_DoesNotDeadlock
```

**Action:** Quarantine (6a)
Tracked by #14488

- **Distinct sources:** 5 (2 PRs + 3 rolling builds)
- **PRs:** #14371, #14401
- **Rolling builds:**
[1511941](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1511941),
[1517682](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1517682),
[1519967](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1519967)
- **Legs/TFMs:** CoreOnMac, FullOnWindows, FullOnWindows Release /
net10.0, net472
- **Assembly:** MSBuild.Coordinator.UnitTests
- **Window:** 2026-07-15 – 2026-07-21
- **Sample error:** `System.TimeoutException : Test failed due to
timeout: process is active for more than 60 sec.`

---

## `TaskExceptionHandlingTest`

**Flaky-test key** (automated de-duplication — do not edit):
```text
flaky-test-id: Microsoft.Build.UnitTests.BackEnd.TaskExecutionHost_Tests.TaskExceptionHandlingTest
```

**Action:** Quarantine (6a)
Tracked by #14459

- **Distinct sources:** 5 (1 PR + 4 rolling builds)
- **PRs:** #14315
- **Rolling builds:**
[1510684](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1510684),
[1517216](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1517216),
[1519472](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1519472),
[1519928](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1519928)
- **Legs/TFMs:** FullOnWindows Release / net472
- **Assembly:** Microsoft.Build.Engine.UnitTests
- **Window:** 2026-07-15 – 2026-07-21
- **Sample error:** `Shouldly.ShouldAssertException` in
`TaskExceptionHandlingTest`




> [!WARNING]
> <details>
> <summary>Firewall blocked 2 domains</summary>
>
> The following domains were blocked by the firewall during workflow
execution:
>
> - `awmgmcpg`
> - `southcentralus0.in.applicationinsights.azure.com`
>> To allow these domains, add them to the `network.allowed` list in
your workflow frontmatter:
>
> ```yaml
> network:
>   allowed:
>     - defaults
>     - "awmgmcpg"
>     - "southcentralus0.in.applicationinsights.azure.com"
> ```
>
> See [Network
Configuration](https://github.github.com/gh-aw/reference/network/) for
more information.
>
> </details>


> Generated by [Flaky Test
Triage](https://github.com/dotnet/msbuild/actions/runs/30006270630) ·
126 AIC · ⌖ 6.2 AIC · ⊞ 14.8K ·
[◷](https://github.com/search?q=repo%3Adotnet%2Fmsbuild+%22gh-aw-workflow-id%3A+flaky-test-detector.agent%22&type=pullrequests)

<!-- gh-aw-agentic-workflow: Flaky Test Triage, engine: copilot,
version: 1.0.70, model: claude-sonnet-4.6, id: 30006270630, workflow_id:
flaky-test-detector.agent, run:
https://github.com/dotnet/msbuild/actions/runs/30006270630 -->

<!-- gh-aw-workflow-id: flaky-test-detector.agent -->
<!-- gh-aw-workflow-call-id: dotnet/msbuild/flaky-test-detector.agent
-->

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+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.

Add analyzer to steer task authors toward constructor-based TaskEnvironment injection

3 participants