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

Fix a number of issues in CA1859. #7133

Merged
merged 4 commits into from
Jan 17, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,70 @@ public static void ReturnInstance(Collector c, CancellationToken cancellationTok
}

/// <summary>
/// Identify fields/locals/params that are used as 'this' for a virtual dispatch.
/// Identify properties that are used as 'this' for a virtual dispatch.
/// </summary>
public void HandlePropertyReference(IPropertyReferenceOperation op)
{
if (op.Property.IsAbstract || op.Property.IsVirtual)
{
if (op.Instance != null)
{
var targetMethod = op.Property.GetMethod ?? op.Property.SetMethod!;

var instance = op.Instance;
if (instance.Kind == OperationKind.ConditionalAccessInstance)
{
var parent = ((IConditionalAccessInstanceOperation)instance).GetConditionalAccess() ?? instance;
if (parent != null)
{
instance = ((IConditionalAccessOperation)parent).Operation;
}
}
geeknoid marked this conversation as resolved.
Show resolved Hide resolved

switch (instance.Kind)
{
case OperationKind.FieldReference:
{
var fieldRef = (IFieldReferenceOperation)instance;
if (CanUpgrade(fieldRef.Field))
{
RecordVirtualDispatch(fieldRef.Field, targetMethod);
}

break;
}

case OperationKind.PropertyReference:
{
var propertyRef = (IPropertyReferenceOperation)instance;
if (CanUpgrade(propertyRef.Property, false))
{
RecordVirtualDispatch(propertyRef.Property, targetMethod);
}

break;
}

case OperationKind.LocalReference:
{
var localRef = (ILocalReferenceOperation)instance;
RecordVirtualDispatch(localRef.Local, targetMethod);
break;
}

case OperationKind.ParameterReference:
{
var parameterRef = (IParameterReferenceOperation)instance;
RecordVirtualDispatch(parameterRef.Parameter, targetMethod);
break;
}
}
}
}
}

/// <summary>
/// Identify fields/properties/locals/params that are used as 'this' for a virtual dispatch.
/// </summary>
public void HandleInvocation(IInvocationOperation op)
{
Expand Down Expand Up @@ -368,6 +431,17 @@ private void GetValueTypes(List<ITypeSymbol> values, IOperation? op)
{
switch (op?.Kind)
{
case OperationKind.Await:
{
if (op.Type != null)
{
values.Add(op.Type);
}

return;

}

case OperationKind.Literal:
{
if (op.HasNullConstantValue())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ public override void Initialize(AnalysisContext context)
context.RegisterOperationAction(context => coll.HandleDeconstructionAssignment((IDeconstructionAssignmentOperation)context.Operation), OperationKind.DeconstructionAssignment);
context.RegisterOperationAction(context => coll.HandleFieldInitializer((IFieldInitializerOperation)context.Operation), OperationKind.FieldInitializer);
context.RegisterOperationAction(context => coll.HandlePropertyInitializer((IPropertyInitializerOperation)context.Operation), OperationKind.PropertyInitializer);
context.RegisterOperationAction(context => coll.HandlePropertyReference((IPropertyReferenceOperation)context.Operation), OperationKind.PropertyReference);
context.RegisterOperationAction(context => coll.HandleVariableDeclarator((IVariableDeclaratorOperation)context.Operation), OperationKind.VariableDeclarator);
context.RegisterOperationAction(context => coll.HandleDeclarationExpression((IDeclarationExpressionOperation)context.Operation), OperationKind.DeclarationExpression);
context.RegisterOperationAction(context => coll.HandleReturn((IReturnOperation)context.Operation), OperationKind.Return);
Expand Down Expand Up @@ -323,9 +324,9 @@ void Evaluate(ISymbol affectedSymbol, ITypeSymbol fromType, PooledConcurrentSet<
}
}

if (toType.TypeKind is not TypeKind.Class and not TypeKind.Array)
if (toType.TypeKind is not TypeKind.Class and not TypeKind.Array and not TypeKind.Struct)
{
// we only deal with classes or arrays
// we only deal with classes, arrays, or structs
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,123 @@ namespace Microsoft.NetCore.Analyzers.Performance.UnitTests
{
public static partial class UseConcreteTypeTests
{
[Fact]
[WorkItem(6904, "https://github.com/dotnet/roslyn-analyzers/issues/6904")]
public static async Task AwaitBug()
{
await TestCSAsync(@"
using System.Threading.Tasks;

public class Class1
{
private I Prop { get; set; } = new Impl1(); //<-- CA1859

public async Task Init()
{
Prop = await Task.FromResult<I>(new Impl2());
Prop.M();
}
}

internal interface I
{
void M();
}

internal class Impl1 : I
{
public void M() { }
}

internal class Impl2 : I
{
public void M() { }
}
");
}

[Fact]
[WorkItem(7078, "https://github.com/dotnet/roslyn-analyzers/issues/7078")]
public static async Task IndexerBug()
{
await TestCSAsync(@"
using System.Collections.Generic;

public struct MailAddress { }

public class C
{
private IList<MailAddress> {|#1:_field|};
private IList<MailAddress> {|#2:Property|} { get; set; }

internal void ParseValue1(string addresses)
{
IList<MailAddress> {|#0:result|} = ParseMultipleAddresses(addresses);

var x = result[0];
}
internal void ParseValue2(string addresses)
{
IList<MailAddress> {|#3:result|} = ParseMultipleAddresses(addresses);

#nullable enable
var x = result?[0];
#nullable disable
}
internal void ParseValue3(string addresses)
{
_field = ParseMultipleAddresses(addresses);

var x = _field[0];
}

internal void ParseValue4(string addresses)
{
Property = ParseMultipleAddresses(addresses);

var x = Property[0];
}

internal static List<MailAddress> ParseMultipleAddresses(string data) => new();
}
",
VerifyCS.Diagnostic(UseConcreteTypeAnalyzer.UseConcreteTypeForLocal)
.WithLocation(0)
.WithArguments("result", "System.Collections.Generic.IList<MailAddress>", "System.Collections.Generic.List<MailAddress>"),
VerifyCS.Diagnostic(UseConcreteTypeAnalyzer.UseConcreteTypeForField)
.WithLocation(1)
.WithArguments("_field", "System.Collections.Generic.IList<MailAddress>", "System.Collections.Generic.List<MailAddress>"),
VerifyCS.Diagnostic(UseConcreteTypeAnalyzer.UseConcreteTypeForProperty)
.WithLocation(2)
.WithArguments("Property", "System.Collections.Generic.IList<MailAddress>", "System.Collections.Generic.List<MailAddress>"),
VerifyCS.Diagnostic(UseConcreteTypeAnalyzer.UseConcreteTypeForLocal)
.WithLocation(3)
.WithArguments("result", "System.Collections.Generic.IList<MailAddress>", "System.Collections.Generic.List<MailAddress>"));
}

[Fact]
[WorkItem(7127, "https://github.com/dotnet/roslyn-analyzers/issues/7127")]
public static async Task ImmutableArrayBug()
{
await TestCSAsync(@"
using System.Collections.Generic;
using System.Collections.Immutable;

public class Class1
{
private IEnumerable<int> {|#0:CreateImmutableArrayPrivately|}()
{
return new ImmutableArray<int>
{
1, 2, 3, 4
};
}
}
", VerifyCS.Diagnostic(UseConcreteTypeAnalyzer.UseConcreteTypeForMethodReturn)
.WithLocation(0)
.WithArguments("CreateImmutableArrayPrivately", "System.Collections.Generic.IEnumerable<int>", "System.Collections.Immutable.ImmutableArray<int>"));
}

[Fact]
[WorkItem(6751, "https://github.com/dotnet/roslyn-analyzers/issues/6751")]
public static async Task MultipleReturns()
Expand Down Expand Up @@ -268,7 +385,7 @@ private static object Test(int arg)
public static async Task ShouldNotTrigger_ValidatePublicSymbolUsage()
{
const string Source = @"
#nullable enable
#nullable enable

using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -382,7 +499,7 @@ await TestCSAsync(source, $"dotnet_code_quality.CA1859.api_surface = {editorConf
public static async Task ShouldNotTrigger1()
{
const string Source = @"
#nullable enable
#nullable enable

using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -416,7 +533,7 @@ public class Derived2 : BaseType
public static async Task ShouldNotTrigger2()
{
const string Source = @"
#nullable enable
#nullable enable

using System.Collections.Generic;

Expand Down Expand Up @@ -458,7 +575,7 @@ public static int Bar()
public static async Task ShouldNotTrigger3()
{
const string Source = @"
#nullable enable
#nullable enable

using System;
using System.IO;
Expand Down Expand Up @@ -489,7 +606,7 @@ public class MyStream : MemoryStream { }
public static async Task ShouldNotTrigger4()
{
const string Source = @"
#nullable enable
#nullable enable

using System;
using System.IO;
Expand Down Expand Up @@ -520,7 +637,7 @@ public class MyStream : MemoryStream { }
public static async Task ShouldNotTrigger5()
{
const string Source = @"
#nullable enable
#nullable enable

interface IFoo
{
Expand Down Expand Up @@ -652,7 +769,7 @@ await TestCSAsync(Source,
public static async Task ShouldTrigger3()
{
const string Source = @"
#nullable enable
#nullable enable

using System;
using System.IO;
Expand Down Expand Up @@ -683,7 +800,7 @@ await TestCSAsync(Source,
public static async Task ShouldTrigger4()
{
const string Source = @"
#nullable enable
#nullable enable

using System;
using System.IO;
Expand Down Expand Up @@ -757,7 +874,7 @@ await TestCSAsync(Source,
public static async Task Conditional()
{
const string Source = @"
#nullable enable
#nullable enable
namespace Example
{
public interface IFoo
Expand Down