Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VSTHRD003 analyzer false positive fixes #1258

Merged
merged 3 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -195,15 +195,30 @@ private void AnalyzeAwaitExpression(SyntaxNodeAnalysisContext context)
}

// Do not report a warning if the task is a member of an object that was created in this method.
if (memberAccessExpression.Expression is IdentifierNameSyntax identifier &&
semanticModel.GetSymbolInfo(identifier, cancellationToken).Symbol is ILocalSymbol local)
if (memberAccessExpression.Expression is IdentifierNameSyntax identifier)
{
// Search for assignments to the local and see if it was to a new object.
containingFunc ??= CSharpUtils.GetContainingFunction(focusedExpression);
if (containingFunc.Value.BlockOrExpression is not null &&
CSharpUtils.FindAssignedValuesWithin(containingFunc.Value.BlockOrExpression, semanticModel, local, cancellationToken).Any(v => v is ObjectCreationExpressionSyntax))
ISymbol? symbol = semanticModel.GetSymbolInfo(identifier, cancellationToken).Symbol;
switch (symbol)
{
return null;
case ILocalSymbol local:
// Search for assignments to the local and see if it was to a new object or the result of an invocation.
containingFunc ??= CSharpUtils.GetContainingFunction(focusedExpression);
if (containingFunc.Value.BlockOrExpression is not null &&
CSharpUtils.FindAssignedValuesWithin(containingFunc.Value.BlockOrExpression, semanticModel, local, cancellationToken).Any(
v => v is ObjectCreationExpressionSyntax or ImplicitObjectCreationExpressionSyntax or InvocationExpressionSyntax))
{
return null;
}

break;
case IParameterSymbol parameter:
// We allow returning members of a parameter in a lambda, to support `.Select(x => x.Completion)` syntax.
if (parameter.ContainingSymbol is IMethodSymbol method && method.MethodKind == MethodKind.AnonymousFunction)
{
return null;
}

break;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ where resourceName.StartsWith(additionalFilePrefix, StringComparison.Ordinal)

protected override ParseOptions CreateParseOptions()
{
return ((CSharpParseOptions)base.CreateParseOptions()).WithLanguageVersion(LanguageVersion.CSharp8);
return ((CSharpParseOptions)base.CreateParseOptions()).WithLanguageVersion(LanguageVersion.CSharp11);
}

private static string ReadManifestResource(Assembly assembly, string resourceName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1310,6 +1310,29 @@ static async Task GetTask()
await CSVerify.VerifyAnalyzerAsync(test);
}

[Fact]
public async Task DoNotReportWarningWhenAwaitingTaskPropertyOfObjectCreatedInContext_TargetTypeCreation()
{
string test = """
using System.Threading.Tasks;

class Test
{
static Task Exec2Async(string executable, params string[] args)
{
Process p = new();
return p.Task;
}
}

class Process
{
public Task Task { get; }
}
""";
await CSVerify.VerifyAnalyzerAsync(test);
}

/// <summary>
/// This is important to allow folks to return jtf.RunAsync(...).Task from a method.
/// </summary>
Expand All @@ -1334,6 +1357,31 @@ static async Task GetTask()
await CSVerify.VerifyAnalyzerAsync(test);
}

[Fact]
public async Task DoNotReportWarningWhenAwaitingTaskPropertyOfObjectReturnedFromMethodViaLocal()
{
var test = """
using System.Threading.Tasks;

class JsonRpc
{
internal static JsonRpc Attach() => throw new System.NotImplementedException();

internal Task Completion { get; }
}

class Tests
{
static async Task ListenAndWait()
{
var jsonRpc = JsonRpc.Attach();
await jsonRpc.Completion;
}
}
""";
await CSVerify.VerifyAnalyzerAsync(test);
}

[Fact]
public async Task ReportWarningWhenAwaitingTaskPropertyThatWasNotSetInContext()
{
Expand All @@ -1354,6 +1402,32 @@ async Task GetTask()
await CSVerify.VerifyAnalyzerAsync(test);
}

[Fact]
public async Task DoNotReportWarningWhenReturningTaskFromLambdaArgument()
{
var test = """
using System.Linq;
using System.Threading.Tasks;

class JsonRpc
{
internal static JsonRpc Attach() => throw new System.NotImplementedException();

internal Task Completion { get; }
}

class Tests
{
static async Task ListenAndWait()
{
JsonRpc[] rpcs = new [] { JsonRpc.Attach(), JsonRpc.Attach() };
await Task.WhenAll(rpcs.Select(r => r.Completion));
}
}
""";
await CSVerify.VerifyAnalyzerAsync(test);
}

private DiagnosticResult CreateDiagnostic(int line, int column, int length) =>
CSVerify.Diagnostic().WithSpan(line, column, line, column + length);
}