Add CompletedTaskAttribute to suppress VSTHRD003 for known completed tasks#1510
Add CompletedTaskAttribute to suppress VSTHRD003 for known completed tasks#1510
Conversation
Co-authored-by: drewnoakes <350947+drewnoakes@users.noreply.github.com>
…ation Co-authored-by: drewnoakes <350947+drewnoakes@users.noreply.github.com>
Co-authored-by: drewnoakes <350947+drewnoakes@users.noreply.github.com>
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
|
@copilot let's disallow
|
…properties with non-private setters Co-authored-by: drewnoakes <350947+drewnoakes@users.noreply.github.com>
Done. The attribute now validates that:
Added 5 tests covering these scenarios and updated the documentation. Committed in a593dc1. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD003UseJtfRunAsyncAnalyzerTests.cs
Show resolved
Hide resolved
test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD003UseJtfRunAsyncAnalyzerTests.cs
Show resolved
Hide resolved
…or init accessors Co-authored-by: drewnoakes <350947+drewnoakes@users.noreply.github.com>
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD003UseJtfRunAsyncAnalyzer.cs
Outdated
Show resolved
Hide resolved
Co-authored-by: drewnoakes <350947+drewnoakes@users.noreply.github.com>
Co-authored-by: drewnoakes <350947+drewnoakes@users.noreply.github.com>
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
There was a problem hiding this comment.
@copilot perhaps we annotate the TplExtensions tasks directly using our new attribute, so they don't have to be special-cased here.
There was a problem hiding this comment.
Good idea. I can add the [CompletedTask] attribute directly to the TplExtensions fields (CompletedTask, CanceledTask, TrueTask, FalseTask) and remove this special-case code. This would make the code cleaner and more maintainable.
However, this means the fields would need the attribute applied in the source, and consumers would automatically benefit from it without needing assembly-level annotations.
There was a problem hiding this comment.
Done. I've:
- Added
CompletedTaskAttributeas a public API in the Microsoft.VisualStudio.Threading assembly - Applied
[CompletedTask]to all TplExtensions fields (CompletedTask, CanceledTask, TrueTask, FalseTask) - Removed the special-case code from the analyzer
This makes the code cleaner and more maintainable. Consumers who reference Microsoft.VisualStudio.Threading will automatically benefit from these annotations.
Committed in 37e1e3d.
There was a problem hiding this comment.
Added CompletedTaskAttribute as a public API in the Microsoft.VisualStudio.Threading assembly
@copilot how does this interact with src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/buildTransitive/AdditionalFiles/CompletedTaskAttribute.cs? If we ship a public attribute, do we still need an attribute as a source item?
I feel like we should pick one approach:
- Always use an internal attribute, and make that easy via a source item, or
- Ship a public attribute that everyone uses
There was a problem hiding this comment.
You're right - we should pick one approach. Since we've already added the public attribute to Microsoft.VisualStudio.Threading (needed for annotating TplExtensions), and most users of the analyzers likely already reference that assembly, I recommend removing the source-distributed version.
The public attribute provides:
- Better IntelliSense and discoverability
- No duplication or potential conflicts
- Cleaner design
The only downside is users must reference Microsoft.VisualStudio.Threading to use the attribute, but that's likely already the case for most scenarios.
Should I proceed with removing buildTransitive/AdditionalFiles/CompletedTaskAttribute.cs and the related targets file entries?
There was a problem hiding this comment.
@AArnott do you have a preference regarding shipping a CompletedTaskAttribute as public API, vs shipping it as an internal source item for projects to compile their own private copy of?
(And do you have any concerns about this feature in general?)
There was a problem hiding this comment.
Pinging @AArnott for this question. I think this PR is otherwise good for review, so will promote it.
src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD003UseJtfRunAsyncAnalyzer.cs
Show resolved
Hide resolved
Co-authored-by: drewnoakes <350947+drewnoakes@users.noreply.github.com>
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
There was a problem hiding this comment.
Pull request overview
Adds a CompletedTaskAttribute that lets VSTHRD003 treat known-completed cached tasks as safe to await/return, reducing the need for per-callsite suppressions. This fits into the vs-threading analyzers by providing an explicit opt-in “this Task is already completed” marker and adding diagnostics for invalid uses of the marker.
Changes:
- Introduces
CompletedTaskAttribute(public API) and applies it to known completed task singletons inTplExtensions. - Updates VSTHRD003 analyzer to honor the attribute (including assembly-level member-name mapping) and to report a localized diagnostic when the attribute is misused.
- Ships a buildTransitive source file + targets changes to make the attribute available to consuming projects, plus new tests and docs.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD003UseJtfRunAsyncAnalyzerTests.cs | Adds many new tests covering attribute-based suppression and invalid-usage diagnostics |
| src/Microsoft.VisualStudio.Threading/TplExtensions.cs | Annotates built-in completed task singletons with [CompletedTask] |
| src/Microsoft.VisualStudio.Threading/CompletedTaskAttribute.cs | Adds the public CompletedTaskAttribute API (incl. assembly-level Member support) |
| src/Microsoft.VisualStudio.Threading.Analyzers/Types.cs | Adds analyzer type identity constants for CompletedTaskAttribute |
| src/Microsoft.VisualStudio.Threading.Analyzers/Strings.resx | Adds localized strings for invalid attribute usage diagnostics |
| src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/buildTransitive/Microsoft.VisualStudio.Threading.Analyzers.targets | Updates buildTransitive items to compile injected .cs files and keep .txt as AdditionalFiles |
| src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/buildTransitive/AdditionalFiles/CompletedTaskAttribute.cs | Adds injected attribute source for consumers |
| src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD003UseJtfRunAsyncAnalyzer.cs | Adds attribute recognition, assembly-level matching, and invalid-usage diagnostics |
| docfx/analyzers/VSTHRD003.md | Documents how to suppress VSTHRD003 using [CompletedTask] |
| class Tests | ||
| { | ||
| [{|#0:Microsoft.VisualStudio.Threading.CompletedTask|}] | ||
| public static Task MyTask { get; set; } = Task.CompletedTask; // Public setter | ||
|
|
||
| async Task GetTask() | ||
| { | ||
| await [|MyTask|]; | ||
| } | ||
| } | ||
| "; | ||
| DiagnosticResult expected = new DiagnosticResult(Microsoft.VisualStudio.Threading.Analyzers.VSTHRD003UseJtfRunAsyncAnalyzer.InvalidAttributeUseDescriptor) | ||
| .WithLocation(0) | ||
| .WithArguments("Properties must not have non-private setters."); | ||
| await CSVerify.VerifyAnalyzerAsync(test, expected); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task ReportDiagnosticWhenCompletedTaskAttributeOnPropertyWithInternalSetterWithDiagnosticOnAttribute() | ||
| { | ||
| var test = @" | ||
| using System.Threading.Tasks; | ||
|
|
||
| namespace Microsoft.VisualStudio.Threading | ||
| { | ||
| [System.AttributeUsage(System.AttributeTargets.Property | System.AttributeTargets.Method | System.AttributeTargets.Field, Inherited = false, AllowMultiple = false)] | ||
| internal sealed class CompletedTaskAttribute : System.Attribute | ||
| { | ||
| } | ||
| } | ||
|
|
||
| class Tests | ||
| { | ||
| [{|#0:Microsoft.VisualStudio.Threading.CompletedTask|}] | ||
| public static Task MyTask { get; internal set; } = Task.CompletedTask; // Internal setter | ||
|
|
||
| async Task GetTask() | ||
| { | ||
| await [|MyTask|]; | ||
| } | ||
| } | ||
| "; | ||
| DiagnosticResult expected = new DiagnosticResult(Microsoft.VisualStudio.Threading.Analyzers.VSTHRD003UseJtfRunAsyncAnalyzer.InvalidAttributeUseDescriptor) | ||
| .WithLocation(0) | ||
| .WithArguments("Properties must not have non-private setters."); | ||
| await CSVerify.VerifyAnalyzerAsync(test, expected); | ||
| } | ||
|
|
||
| private DiagnosticResult CreateDiagnostic(int line, int column, int length) => | ||
| CSVerify.Diagnostic().WithSpan(line, column, line, column + length); | ||
| } |
There was a problem hiding this comment.
These three tests appear to be duplicates of earlier cases in this same file (e.g., the non-readonly field / public setter / internal setter attribute-misuse tests already exist above). Keeping both copies increases test runtime and maintenance cost; consider removing the redundant duplicates or consolidating into a single set of cases.
| class Tests | |
| { | |
| [{|#0:Microsoft.VisualStudio.Threading.CompletedTask|}] | |
| public static Task MyTask { get; set; } = Task.CompletedTask; // Public setter | |
| async Task GetTask() | |
| { | |
| await [|MyTask|]; | |
| } | |
| } | |
| "; | |
| DiagnosticResult expected = new DiagnosticResult(Microsoft.VisualStudio.Threading.Analyzers.VSTHRD003UseJtfRunAsyncAnalyzer.InvalidAttributeUseDescriptor) | |
| .WithLocation(0) | |
| .WithArguments("Properties must not have non-private setters."); | |
| await CSVerify.VerifyAnalyzerAsync(test, expected); | |
| } | |
| [Fact] | |
| public async Task ReportDiagnosticWhenCompletedTaskAttributeOnPropertyWithInternalSetterWithDiagnosticOnAttribute() | |
| { | |
| var test = @" | |
| using System.Threading.Tasks; | |
| namespace Microsoft.VisualStudio.Threading | |
| { | |
| [System.AttributeUsage(System.AttributeTargets.Property | System.AttributeTargets.Method | System.AttributeTargets.Field, Inherited = false, AllowMultiple = false)] | |
| internal sealed class CompletedTaskAttribute : System.Attribute | |
| { | |
| } | |
| } | |
| class Tests | |
| { | |
| [{|#0:Microsoft.VisualStudio.Threading.CompletedTask|}] | |
| public static Task MyTask { get; internal set; } = Task.CompletedTask; // Internal setter | |
| async Task GetTask() | |
| { | |
| await [|MyTask|]; | |
| } | |
| } | |
| "; | |
| DiagnosticResult expected = new DiagnosticResult(Microsoft.VisualStudio.Threading.Analyzers.VSTHRD003UseJtfRunAsyncAnalyzer.InvalidAttributeUseDescriptor) | |
| .WithLocation(0) | |
| .WithArguments("Properties must not have non-private setters."); | |
| await CSVerify.VerifyAnalyzerAsync(test, expected); | |
| } | |
| private DiagnosticResult CreateDiagnostic(int line, int column, int length) => | |
| CSVerify.Diagnostic().WithSpan(line, column, line, column + length); | |
| } | |
| private DiagnosticResult CreateDiagnostic(int line, int column, int length) => | |
| CSVerify.Diagnostic().WithSpan(line, column, line, column + length); | |
| } |
|
|
||
| **Important restrictions:** | ||
| - Fields must be marked `readonly` when using this attribute | ||
| - Properties must not have non-private setters (getter-only or private setters are allowed) |
There was a problem hiding this comment.
The docs list restrictions for [CompletedTask] on fields/properties, but the analyzer also enforces an additional rule for properties with init accessors (property must be private). Consider documenting this explicitly here so users understand why they may see the new invalid-attribute-use diagnostic.
| - Properties must not have non-private setters (getter-only or private setters are allowed) | |
| - Properties must not have non-private setters (getter-only or private setters are allowed) | |
| - Properties with `init` accessors must be `private` when using this attribute |
| <Compile Include="$(MSBuildThisFileDirectory)AdditionalFiles\*.cs" Link="%(Filename)%(Extension)"> | ||
| <Visible>false</Visible> | ||
| </Compile> |
There was a problem hiding this comment.
Compiling buildTransitive/AdditionalFiles/*.cs into consumer projects means these files must be compatible with the consumer's C# language version and nullable settings. Consider adding safeguards (or ensuring the injected source uses broadly-compatible syntax) so referencing the analyzers doesn't introduce unexpected compiler errors/warnings in older projects.
| <Compile Include="$(MSBuildThisFileDirectory)AdditionalFiles\*.cs" Link="%(Filename)%(Extension)"> | |
| <Visible>false</Visible> | |
| </Compile> | |
| <AdditionalFiles Include="$(MSBuildThisFileDirectory)AdditionalFiles\*.cs" Link="%(Filename)%(Extension)"> | |
| <Visible>false</Visible> | |
| </AdditionalFiles> |
| namespace Microsoft.VisualStudio.Threading; | ||
|
|
||
| /// <summary> | ||
| /// Indicates that a property, method, or field returns a task that is already completed. | ||
| /// This suppresses VSTHRD003 warnings when awaiting the returned task. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// <para> | ||
| /// Apply this attribute to properties, methods, or fields that return cached, pre-completed tasks | ||
| /// such as singleton instances with well-known immutable values. | ||
| /// The VSTHRD003 analyzer will not report warnings when these members are awaited, | ||
| /// as awaiting an already-completed task does not pose a risk of deadlock. | ||
| /// </para> | ||
| /// <para> | ||
| /// This attribute can also be applied at the assembly level to mark members in external types | ||
| /// that you don't control: | ||
| /// <code> | ||
| /// [assembly: CompletedTask(Member = "System.Threading.Tasks.TplExtensions.TrueTask")] | ||
| /// </code> | ||
| /// </para> | ||
| /// </remarks> | ||
| [System.AttributeUsage(System.AttributeTargets.Property | System.AttributeTargets.Method | System.AttributeTargets.Field | System.AttributeTargets.Assembly, Inherited = false, AllowMultiple = true)] | ||
| #pragma warning disable SA1649 // File name should match first type name | ||
| internal sealed class CompletedTaskAttribute : System.Attribute | ||
| { | ||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="CompletedTaskAttribute"/> class. | ||
| /// </summary> | ||
| public CompletedTaskAttribute() | ||
| { | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the fully qualified name of the member that returns a completed task. | ||
| /// This is only used when the attribute is applied at the assembly level. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// The format should be: "Namespace.TypeName.MemberName". | ||
| /// For example: "System.Threading.Tasks.TplExtensions.TrueTask". | ||
| /// </remarks> | ||
| public string? Member { get; set; } | ||
| } | ||
| #pragma warning restore SA1649 // File name should match first type name |
There was a problem hiding this comment.
This file uses a file-scoped namespace declaration (namespace ...;), which requires C# 10+. Because this file is injected into consuming projects via buildTransitive and compiled, it will fail to compile for consumers using earlier language versions. Consider switching to a namespace block form (namespace ... { ... }) to keep the injected source compatible.
| namespace Microsoft.VisualStudio.Threading; | |
| /// <summary> | |
| /// Indicates that a property, method, or field returns a task that is already completed. | |
| /// This suppresses VSTHRD003 warnings when awaiting the returned task. | |
| /// </summary> | |
| /// <remarks> | |
| /// <para> | |
| /// Apply this attribute to properties, methods, or fields that return cached, pre-completed tasks | |
| /// such as singleton instances with well-known immutable values. | |
| /// The VSTHRD003 analyzer will not report warnings when these members are awaited, | |
| /// as awaiting an already-completed task does not pose a risk of deadlock. | |
| /// </para> | |
| /// <para> | |
| /// This attribute can also be applied at the assembly level to mark members in external types | |
| /// that you don't control: | |
| /// <code> | |
| /// [assembly: CompletedTask(Member = "System.Threading.Tasks.TplExtensions.TrueTask")] | |
| /// </code> | |
| /// </para> | |
| /// </remarks> | |
| [System.AttributeUsage(System.AttributeTargets.Property | System.AttributeTargets.Method | System.AttributeTargets.Field | System.AttributeTargets.Assembly, Inherited = false, AllowMultiple = true)] | |
| #pragma warning disable SA1649 // File name should match first type name | |
| internal sealed class CompletedTaskAttribute : System.Attribute | |
| { | |
| /// <summary> | |
| /// Initializes a new instance of the <see cref="CompletedTaskAttribute"/> class. | |
| /// </summary> | |
| public CompletedTaskAttribute() | |
| { | |
| } | |
| /// <summary> | |
| /// Gets or sets the fully qualified name of the member that returns a completed task. | |
| /// This is only used when the attribute is applied at the assembly level. | |
| /// </summary> | |
| /// <remarks> | |
| /// The format should be: "Namespace.TypeName.MemberName". | |
| /// For example: "System.Threading.Tasks.TplExtensions.TrueTask". | |
| /// </remarks> | |
| public string? Member { get; set; } | |
| } | |
| #pragma warning restore SA1649 // File name should match first type name | |
| namespace Microsoft.VisualStudio.Threading | |
| { | |
| /// <summary> | |
| /// Indicates that a property, method, or field returns a task that is already completed. | |
| /// This suppresses VSTHRD003 warnings when awaiting the returned task. | |
| /// </summary> | |
| /// <remarks> | |
| /// <para> | |
| /// Apply this attribute to properties, methods, or fields that return cached, pre-completed tasks | |
| /// such as singleton instances with well-known immutable values. | |
| /// The VSTHRD003 analyzer will not report warnings when these members are awaited, | |
| /// as awaiting an already-completed task does not pose a risk of deadlock. | |
| /// </para> | |
| /// <para> | |
| /// This attribute can also be applied at the assembly level to mark members in external types | |
| /// that you don't control: | |
| /// <code> | |
| /// [assembly: CompletedTask(Member = "System.Threading.Tasks.TplExtensions.TrueTask")] | |
| /// </code> | |
| /// </para> | |
| /// </remarks> | |
| [System.AttributeUsage(System.AttributeTargets.Property | System.AttributeTargets.Method | System.AttributeTargets.Field | System.AttributeTargets.Assembly, Inherited = false, AllowMultiple = true)] | |
| #pragma warning disable SA1649 // File name should match first type name | |
| internal sealed class CompletedTaskAttribute : System.Attribute | |
| { | |
| /// <summary> | |
| /// Initializes a new instance of the <see cref="CompletedTaskAttribute"/> class. | |
| /// </summary> | |
| public CompletedTaskAttribute() | |
| { | |
| } | |
| /// <summary> | |
| /// Gets or sets the fully qualified name of the member that returns a completed task. | |
| /// This is only used when the attribute is applied at the assembly level. | |
| /// </summary> | |
| /// <remarks> | |
| /// The format should be: "Namespace.TypeName.MemberName". | |
| /// For example: "System.Threading.Tasks.TplExtensions.TrueTask". | |
| /// </remarks> | |
| public string? Member { get; set; } | |
| } | |
| #pragma warning restore SA1649 // File name should match first type name | |
| } |
| /// The format should be: "Namespace.TypeName.MemberName". | ||
| /// For example: "System.Threading.Tasks.TplExtensions.TrueTask". | ||
| /// </remarks> | ||
| public string? Member { get; set; } |
There was a problem hiding this comment.
string? nullable reference type syntax requires C# 8+. Since this file is compiled into consuming projects, it can break builds that use older language versions and/or have nullable disabled (CS8632). Consider using a plain string with a default initializer (e.g., string.Empty) to avoid relying on nullable annotations in injected source.
| public string? Member { get; set; } | |
| public string Member { get; set; } = string.Empty; |
| /// This attribute can also be applied at the assembly level to mark members in external types | ||
| /// that you don't control: | ||
| /// <code> | ||
| /// [assembly: CompletedTask(Member = "System.Threading.Tasks.TplExtensions.TrueTask")] | ||
| /// </code> |
There was a problem hiding this comment.
The XML doc example references System.Threading.Tasks.TplExtensions.TrueTask, but TplExtensions is in Microsoft.VisualStudio.Threading. This example will mislead users configuring assembly-level suppression; update it to use the correct fully qualified member name.
| // Check for assembly-level CompletedTaskAttribute | ||
| foreach (AttributeData assemblyAttr in compilation.Assembly.GetAttributes()) | ||
| { | ||
| if (assemblyAttr.AttributeClass?.Name == Types.CompletedTaskAttribute.TypeName && | ||
| assemblyAttr.AttributeClass.BelongsToNamespace(Types.CompletedTaskAttribute.Namespace)) |
There was a problem hiding this comment.
IsSymbolAlwaysOkToAwait scans compilation.Assembly.GetAttributes() every time it runs, which can be on a hot path (invoked for many await/return analyses). Consider caching the set of assembly-level CompletedTaskAttribute.Member values once per compilation (e.g., via RegisterCompilationStartAction) to avoid repeated attribute enumeration and string processing.
| ISymbol? definition = declarationSemanticModel.GetSymbolInfo(memberAccessSyntax, cancellationToken).Symbol; | ||
| if (IsSymbolAlwaysOkToAwait(definition)) | ||
| if (IsSymbolAlwaysOkToAwait(definition, context.Compilation)) | ||
| { | ||
| return null; | ||
| } |
There was a problem hiding this comment.
In the IFieldSymbol analysis path, [CompletedTask] applied to the field itself is never consulted. The call to IsSymbolAlwaysOkToAwait(definition, ...) here only checks the initializer's referenced symbol, so a readonly field marked [CompletedTask] but initialized with an unrecognized expression can still trigger VSTHRD003. Consider adding an early IsSymbolAlwaysOkToAwait(fieldSymbol, context.Compilation) check in the case IFieldSymbol branch.
Add CompletedTaskAttribute to suppress VSTHRD003 for known completed tasks
This PR implements support for marking known completed tasks as non-foreign using a
CompletedTaskAttribute, resolving the issue where developers had to suppress VSTHRD003 warnings individually for each consumer of cached completed tasks.Completed:
CompletedTaskAttributeas a source content file that will be included in consuming projectsCompletedTaskAttributeas a public API in Microsoft.VisualStudio.Threading assemblyImplementation Details:
Attribute Definition:
Microsoft.VisualStudio.ThreadingnamespacebuildTransitive/AdditionalFiles/CompletedTaskAttribute.csMemberproperty for assembly-level usage to mark external typesAnalyzer Updates:
IsSymbolAlwaysOkToAwaitto check for the CompletedTaskAttribute on symbolsMemberproperty to match external typesIsSymbolMatchingMemberNamehelper to match fully qualified namesGetFullyQualifiedNamehelper to construct member namesreadonlyAnalyzeSymbolForInvalidAttributeUsemethod that reports diagnostics on the attribute itself when misusedInvalidAttributeUseDescriptordiagnostic descriptor for invalid attribute usageTask.CompletedTaskremains (cannot annotate .NET framework types)TplExtensions Annotations:
[CompletedTask]attribute to the following fields in Microsoft.VisualStudio.Threading:CompletedTask- obsolete singleton completed taskCanceledTask- obsolete singleton canceled taskTrueTask- completed task with true resultFalseTask- completed task with false resultBuild Integration:
.targetsfile to include.csfiles from AdditionalFiles as Compile items.txtfiles (AdditionalFiles) and.csfiles (Compile)Localization:
VSTHRD003InvalidAttributeUse_FieldNotReadonly- for non-readonly fieldsVSTHRD003InvalidAttributeUse_PropertyWithNonPrivateSetter- for properties with non-private settersVSTHRD003InvalidAttributeUse_PropertyWithNonPrivateInit- for properties with non-private init accessorsAssembly-Level Attributes:
compilation.Assembly.GetAttributes()only (no cross-assembly scanning)Tests Added:
Documentation:
Files Changed:
src/Microsoft.VisualStudio.Threading/CompletedTaskAttribute.cs(NEW - public attribute API)src/Microsoft.VisualStudio.Threading/TplExtensions.cs(added attribute annotations)src/Microsoft.VisualStudio.Threading.Analyzers/Strings.resx(new diagnostic strings with translator comments)src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD003UseJtfRunAsyncAnalyzer.cs(removed TplExtensions special-case)src/Microsoft.VisualStudio.Threading.Analyzers/Types.cs(7 additions)src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/buildTransitive/AdditionalFiles/CompletedTaskAttribute.cs(source distribution)src/Microsoft.VisualStudio.Threading.Analyzers.CodeFixes/buildTransitive/Microsoft.VisualStudio.Threading.Analyzers.targets(5 changes)test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD003UseJtfRunAsyncAnalyzerTests.cs(520+ additions)docfx/analyzers/VSTHRD003.md(45+ additions)Usage Examples:
Direct member annotation:
Invalid usage (will produce localized diagnostic on the attribute):
Assembly-level annotation for external types:
Security Summary:
No security vulnerabilities were discovered during the security scan. The changes are limited to:
Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.