Skip to content

Add Roslyn analyzers (MSBuildTask0006/0007) to suggest typed task parameters#13972

Draft
baronfel wants to merge 14 commits into
typed-params/corefrom
typed-params/migration-analyzers
Draft

Add Roslyn analyzers (MSBuildTask0006/0007) to suggest typed task parameters#13972
baronfel wants to merge 14 commits into
typed-params/corefrom
typed-params/migration-analyzers

Conversation

@baronfel
Copy link
Copy Markdown
Member

@baronfel baronfel commented Jun 5, 2026

Summary

Adds two Roslyn analyzers to the MSBuild task analyzer package that suggest migration from string to typed task parameters (AbsolutePath/FileInfo/DirectoryInfo/ITaskItem):

  • MSBuildTask0006 (PreferTypedPathParameter): Fires when a task property uses
    ew AbsolutePath(Prop),
    ew FileInfo(Prop), or similar patterns, suggesting the property be retyped to the stronger type.
  • MSBuildTask0007 (PreferTypedTaskItem): Fires when item.ItemSpec is passed to a path/type constructor, suggesting the property use ITaskItem instead.

Both analyzers include code-fix providers that automatically apply the suggested retyping.

Analyzers are restricted to multithreaded tasks only (classes implementing IMultiThreadableTask).

Stacked on

/cc @baronfel

@baronfel baronfel changed the title typed params/migration analyzers Add Roslyn analyzers (MSBuildTask0006/0007) to suggest typed task parameters Jun 5, 2026
@baronfel baronfel force-pushed the typed-params/migration-analyzers branch from 012beff to f4d5fe0 Compare June 5, 2026 21:11
baronfel and others added 14 commits June 5, 2026 16:22
MSBuildTask0006 (Info): Suggests using AbsolutePath, FileInfo, or
DirectoryInfo as task parameter types instead of converting from string
inside the task body.

MSBuildTask0007 (Info): Suggests using ITaskItem<T> instead of manually
parsing ItemSpec to value types, AbsolutePath, FileInfo, or
DirectoryInfo.

Both analyzers detect patterns with direct property references, one
level of local variable indirection, foreach iteration variables, and
array element access. [Output] properties and non-public properties
are excluded from detection.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add detection of TaskEnvironment.GetAbsolutePath(item.ItemSpec) pattern
to MSBuildTask0007, suggesting ITaskItem<AbsolutePath> instead of manual
ItemSpec parsing. This catches the most common pattern in existing tasks.

Verified: analyzer produces 10 MSBuildTask0006 and 52 MSBuildTask0007
hits across the existing MSBuild tasks codebase.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The analyzers now require both:
1. The class implements ITask (is actually a task)
2. The class opts into multithreaded support (IMultiThreadableTask or
   [MSBuildMultiThreadableTask] attribute)

This targets the analyzers at tasks that can actually use the new typed
parameter features. Updated tests to use [MSBuildMultiThreadableTask] on
positive cases, added negative tests for non-MT tasks and MT-attributed
non-task classes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extend MSBuildTask0006 and MSBuildTask0007 to detect two previously-missed
patterns:

1. Path.Combine with task input arguments: any argument to
   System.IO.Path.Combine that traces to a string task property
   (MSBuildTask0006) or ITaskItem.ItemSpec (MSBuildTask0007) now fires.

2. Helper method wrapping: FindItemSpecSource and FindSourceProperty now
   trace through one level of method call wrapping (e.g.,
   FileUtilities.FixFilePath(item.ItemSpec)) to find the original
   task property source.

These changes increase detection from 19 to 58 unique locations across
the existing MT-enabled tasks in the codebase.

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

Extend MSBuildTask0007 to detect patterns like:
  AbsolutePath abs = TaskEnvironment.GetAbsolutePath(item.ItemSpec);
  new FileInfo(abs);

The analyzer now suggests ITaskItem<FileInfo> or ITaskItem<DirectoryInfo>
instead of ITaskItem<AbsolutePath> when the AbsolutePath is only used
as an intermediary to construct FileInfo/DirectoryInfo.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When an ITaskItem[] property is detected, the analyzer now suggests
ITaskItem<T>[] instead of ITaskItem<T>. For example, MakeDir.Directories
now correctly suggests ITaskItem<AbsolutePath>[] instead of
ITaskItem<AbsolutePath>.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The diagnostic message now correctly formats array suggestions as
'ITaskItem<T>[]' instead of 'ITaskItem<T[]>'. For example, RemoveDir's
Directories property now suggests 'ITaskItem<AbsolutePath>[]' with the
brackets outside the angle brackets.

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

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

When the analyzer detects both GetAbsolutePath(item.ItemSpec) and a
downstream new FileInfo(abs)/new DirectoryInfo(abs) for the same
property, the AbsolutePath diagnostic is suppressed in favor of the
more specific FileInfo/DirectoryInfo suggestion. This is done by
collecting diagnostics during operation analysis and deduplicating
in RegisterSymbolEndAction.

For example, ZipDirectory.SourceDirectory now only suggests
ITaskItem<DirectoryInfo> instead of both ITaskItem<AbsolutePath>
and ITaskItem<DirectoryInfo>.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…and Path.Combine to any argument

When a task input's rooted path flows into a System.IO.File.* call, a FileStream/StreamReader/StreamWriter
constructor, or a System.IO.Directory.* call, suggest ITaskItem<FileInfo>/ITaskItem<DirectoryInfo> directly
rather than the more generic ITaskItem<AbsolutePath>. Tracing follows both the direct ItemSpec and an
AbsolutePath intermediary, including locals that are declared then assigned once (the common
'AbsolutePath? p = null; try { p = GetAbsolutePath(...); }' pattern).

Path.Combine now flags every distinct task input flowing into any argument position, not just the first.

Deduplication is reworked to use structured diagnostic properties instead of parsing messages, and resolves
contradictory file-vs-directory inference by falling back to AbsolutePath.

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

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
GetParsedTypeFromMethod previously accepted any value type with a
Parse/TryParse method (e.g. Guid, TimeSpan, DateTimeOffset). These
types are not supported by ValueTypeParser, so suggesting ITaskItem<Guid>
would result in a runtime failure. Restrict suggestions to only the
types ValueTypeParser actually handles.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@baronfel baronfel force-pushed the typed-params/migration-analyzers branch from f4d5fe0 to 6b6a7d7 Compare June 5, 2026 21:22
@ly6093909-ops
Copy link
Copy Markdown

13972

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.

2 participants