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

VSTHRD103 should flag violations amidst use of ?. expressions #1242

Merged
merged 2 commits into from
Oct 24, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" />
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="$(CodeAnalysisVersion)" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="$(CodeAnalysisVersion)" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing" Version="$(CodefixTestingVersion)" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="$(CodefixTestingVersion)" />
<PackageVersion Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" Version="3.3.0-beta2.final" />
<PackageVersion Include="Microsoft.CodeAnalysis.VisualBasic" Version="$(CodeAnalysisVersion)" />
<PackageVersion Include="Microsoft.CodeAnalysis.VisualBasic.CodeFix.Testing" Version="$(CodefixTestingVersion)" />
<PackageVersion Include="Microsoft.CodeAnalysis.VisualBasic.CodeFix.Testing.XUnit" Version="$(CodefixTestingVersion)" />
<PackageVersion Include="Microsoft.Diagnostics.Runtime.Utilities" Version="$(MicrosoftDiagnosticsRuntimeVersion)" />
<PackageVersion Include="Microsoft.Diagnostics.Runtime" Version="$(MicrosoftDiagnosticsRuntimeVersion)" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ public override void Initialize(AnalysisContext context)
{
ctxt.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(MethodAnalyzer.AnalyzeInvocation), SyntaxKind.InvocationExpression);
ctxt.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(MethodAnalyzer.AnalyzePropertyGetter), SyntaxKind.SimpleMemberAccessExpression);
ctxt.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(MethodAnalyzer.AnalyzeConditionalAccessExpression), SyntaxKind.ConditionalAccessExpression);
});
}

Expand All @@ -82,7 +83,21 @@ internal static void AnalyzePropertyGetter(SyntaxNodeAnalysisContext context)
var memberAccessSyntax = (MemberAccessExpressionSyntax)context.Node;
if (IsInTaskReturningMethodOrDelegate(context))
{
InspectMemberAccess(context, memberAccessSyntax, CommonInterest.SyncBlockingProperties);
InspectMemberAccess(context, memberAccessSyntax.Name, CommonInterest.SyncBlockingProperties);
}
}

internal static void AnalyzeConditionalAccessExpression(SyntaxNodeAnalysisContext context)
{
var conditionalAccessSyntax = (ConditionalAccessExpressionSyntax)context.Node;
if (IsInTaskReturningMethodOrDelegate(context))
{
ExpressionSyntax rightSide = conditionalAccessSyntax.WhenNotNull switch
{
MemberBindingExpressionSyntax bindingExpr => bindingExpr.Name,
_ => conditionalAccessSyntax.WhenNotNull,
};
InspectMemberAccess(context, rightSide, CommonInterest.SyncBlockingProperties);
}
}

Expand All @@ -92,7 +107,7 @@ internal static void AnalyzeInvocation(SyntaxNodeAnalysisContext context)
{
var invocationExpressionSyntax = (InvocationExpressionSyntax)context.Node;
var memberAccessSyntax = invocationExpressionSyntax.Expression as MemberAccessExpressionSyntax;
if (InspectMemberAccess(context, memberAccessSyntax, CommonInterest.SyncBlockingMethods))
if (memberAccessSyntax is not null && InspectMemberAccess(context, memberAccessSyntax.Name, CommonInterest.SyncBlockingMethods))
{
// Don't return double-diagnostics.
return;
Expand Down Expand Up @@ -174,21 +189,16 @@ private static bool IsInTaskReturningMethodOrDelegate(SyntaxNodeAnalysisContext
return methodSymbol.HasAsyncCompatibleReturnType();
}

private static bool InspectMemberAccess(SyntaxNodeAnalysisContext context, [NotNullWhen(true)] MemberAccessExpressionSyntax? memberAccessSyntax, IEnumerable<CommonInterest.SyncBlockingMethod> problematicMethods)
private static bool InspectMemberAccess(SyntaxNodeAnalysisContext context, ExpressionSyntax memberName, IEnumerable<CommonInterest.SyncBlockingMethod> problematicMethods)
{
if (memberAccessSyntax is null)
{
return false;
}

ISymbol? memberSymbol = context.SemanticModel.GetSymbolInfo(memberAccessSyntax, context.CancellationToken).Symbol;
ISymbol? memberSymbol = context.SemanticModel.GetSymbolInfo(memberName, context.CancellationToken).Symbol;
if (memberSymbol is object)
{
foreach (CommonInterest.SyncBlockingMethod item in problematicMethods)
{
if (item.Method.IsMatch(memberSymbol))
{
Location? location = memberAccessSyntax.Name.GetLocation();
Location? location = memberName.GetLocation();
ImmutableDictionary<string, string?>? properties = ImmutableDictionary<string, string?>.Empty
.Add(ExtensionMethodNamespaceKeyName, item.ExtensionMethodNamespace is object ? string.Join(".", item.ExtensionMethodNamespace) : string.Empty);
DiagnosticDescriptor descriptor;
Expand Down

Choose a reason for hiding this comment

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

nit: line 62 comment, consider moving this above the if.

Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
SemanticModel? semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
SyntaxNode syntaxRoot = await context.Document.GetSyntaxRootOrThrowAsync(context.CancellationToken).ConfigureAwait(false);
var blockingIdentifier = syntaxRoot.FindNode(diagnostic.Location.SourceSpan) as IdentifierNameSyntax;
if (blockingIdentifier?.Parent is MemberBindingExpressionSyntax) // ?. conditional access expressions.
{
// Fixes for these are complex, and the violations rare. So we won't automate a code fix for them.
return;
}

var memberAccessExpression = blockingIdentifier?.Parent as MemberAccessExpressionSyntax;

// Check whether this code was already calling the awaiter (in a synchronous fashion).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,14 @@
#endif
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing.Verifiers;
using Microsoft.CodeAnalysis.Text;
using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider;

namespace Microsoft.VisualStudio.Threading.Analyzers.Tests;

public static partial class CSharpCodeFixVerifier<TAnalyzer, TCodeFix>
{
public class Test : CSharpCodeFixTest<TAnalyzer, TCodeFix, XUnitVerifier>
public class Test : CSharpCodeFixTest<TAnalyzer, TCodeFix, DefaultVerifier>
{
public Test()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Testing.Verifiers;

namespace Microsoft.VisualStudio.Threading.Analyzers.Tests;

Expand All @@ -13,10 +12,10 @@ public static partial class CSharpCodeFixVerifier<TAnalyzer, TCodeFix>
where TCodeFix : CodeFixProvider, new()
{
public static DiagnosticResult Diagnostic()
=> CSharpCodeFixVerifier<TAnalyzer, TCodeFix, XUnitVerifier>.Diagnostic();
=> CSharpCodeFixVerifier<TAnalyzer, TCodeFix, DefaultVerifier>.Diagnostic();

public static DiagnosticResult Diagnostic(string diagnosticId)
=> CSharpCodeFixVerifier<TAnalyzer, TCodeFix, XUnitVerifier>.Diagnostic(diagnosticId);
=> CSharpCodeFixVerifier<TAnalyzer, TCodeFix, DefaultVerifier>.Diagnostic(diagnosticId);

public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor)
=> new DiagnosticResult(descriptor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
#if WINDOWS
using System.Windows.Threading;
#endif
using Microsoft.CodeAnalysis.Testing.Verifiers;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.VisualBasic;
using Microsoft.CodeAnalysis.VisualBasic.Testing;
Expand All @@ -18,7 +17,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers.Tests;

public static partial class VisualBasicCodeFixVerifier<TAnalyzer, TCodeFix>
{
public class Test : VisualBasicCodeFixTest<TAnalyzer, TCodeFix, XUnitVerifier>
public class Test : VisualBasicCodeFixTest<TAnalyzer, TCodeFix, DefaultVerifier>
{
public Test()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Testing.Verifiers;
using Microsoft.CodeAnalysis.VisualBasic.Testing;

namespace Microsoft.VisualStudio.Threading.Analyzers.Tests;
Expand All @@ -13,10 +12,10 @@ public static partial class VisualBasicCodeFixVerifier<TAnalyzer, TCodeFix>
where TCodeFix : CodeFixProvider, new()
{
public static DiagnosticResult Diagnostic()
=> VisualBasicCodeFixVerifier<TAnalyzer, TCodeFix, XUnitVerifier>.Diagnostic();
=> VisualBasicCodeFixVerifier<TAnalyzer, TCodeFix, DefaultVerifier>.Diagnostic();

public static DiagnosticResult Diagnostic(string diagnosticId)
=> VisualBasicCodeFixVerifier<TAnalyzer, TCodeFix, XUnitVerifier>.Diagnostic(diagnosticId);
=> VisualBasicCodeFixVerifier<TAnalyzer, TCodeFix, DefaultVerifier>.Diagnostic(diagnosticId);

public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor)
=> new DiagnosticResult(descriptor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
<ProjectReference Include="..\..\src\Microsoft.VisualStudio.Threading\Microsoft.VisualStudio.Threading.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" />
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic.CodeFix.Testing.XUnit" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing" />
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic.CodeFix.Testing" />
<PackageReference Include="Microsoft.CodeAnalysis" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Microsoft.VisualStudio.Interop" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ public async Task TaskOfTResultInTaskReturningMethodGeneratesWarning()
class Test {
Task<int> T() {
Task<int> t = null;
int result = t.Result;
int result = t.{|#0:Result|};
return Task.FromResult(result);
}
}
Expand All @@ -427,10 +427,48 @@ async Task<int> T() {
}
";

DiagnosticResult expected = CSVerify.Diagnostic(DescriptorNoAlternativeMethod).WithLocation(7, 24).WithArguments("Result");
DiagnosticResult expected = CSVerify.Diagnostic(DescriptorNoAlternativeMethod).WithLocation(0).WithArguments("Result");
await CSVerify.VerifyCodeFixAsync(test, expected, withFix);
}

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

class Test {
Task<int?> T() {
Task<int> t = null;
int? result = t?.{|#0:Result|};
return Task.FromResult(result);
}
}
";

DiagnosticResult expected = CSVerify.Diagnostic(DescriptorNoAlternativeMethod).WithLocation(0).WithArguments("Result");
await CSVerify.VerifyAnalyzerAsync(test, expected);
}

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

class Test {
Task<int> T() {
Task<int> t = null;
int result = t?.{|#0:Result|} ?? 1;
return Task.FromResult(result);
}
}
";

DiagnosticResult expected = CSVerify.Diagnostic(DescriptorNoAlternativeMethod).WithLocation(0).WithArguments("Result");
await CSVerify.VerifyAnalyzerAsync(test, expected);
}

[Fact]
public async Task TaskOfTResultInTaskReturningMethodGeneratesWarning_FixPreservesCall()
{
Expand Down