Skip to content

Commit

Permalink
Updates
Browse files Browse the repository at this point in the history
  • Loading branch information
Martin Taillefer committed Jan 4, 2023
1 parent 94d6cc4 commit 5fe2c65
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 40 deletions.
Expand Up @@ -136,7 +136,7 @@ public void HandleInvocation(IInvocationOperation op)
{
if (arg.Parameter != null)
{
if (arg.Parameter.RefKind == RefKind.Ref || arg.Parameter.RefKind == RefKind.Out)
if (arg.Parameter.RefKind is RefKind.Ref or RefKind.Out)
{
RecordAssignment(arg.Value, arg.Parameter.Type);
}
Expand Down Expand Up @@ -403,6 +403,13 @@ private void GetValueTypes(List<ITypeSymbol> values, IOperation op)

return;
}

case OperationKind.InstanceReference:
{
var instRef = (IInstanceReferenceOperation)op;
values.Add(instRef.Type!);
return;
}
}
}

Expand Down Expand Up @@ -454,25 +461,10 @@ private void RecordAssignment(IOperation op, ITypeSymbol valueType)
}
}

private void RecordAssignment(IFieldSymbol field, ITypeSymbol valueType)
{
FieldAssignments.GetOrAdd(field, _ => PooledConcurrentSet<ITypeSymbol>.GetInstance(SymbolEqualityComparer.Default)).Add(valueType);
}

private void RecordAssignment(ILocalSymbol local, ITypeSymbol valueType)
{
LocalAssignments.GetOrAdd(local, _ => PooledConcurrentSet<ITypeSymbol>.GetInstance(SymbolEqualityComparer.Default)).Add(valueType);
}

private void RecordAssignment(IParameterSymbol parameter, ITypeSymbol valueType)
{
ParameterAssignments.GetOrAdd(parameter, _ => PooledConcurrentSet<ITypeSymbol>.GetInstance(SymbolEqualityComparer.Default)).Add(valueType);
}

private void RecordAssignment(IMethodSymbol method, ITypeSymbol valueType)
{
MethodReturns.GetOrAdd(method, _ => PooledConcurrentSet<ITypeSymbol>.GetInstance(SymbolEqualityComparer.Default)).Add(valueType);
}
private void RecordAssignment(IFieldSymbol field, ITypeSymbol valueType) => FieldAssignments.GetOrAdd(field, _ => PooledConcurrentSet<ITypeSymbol>.GetInstance(SymbolEqualityComparer.Default)).Add(valueType);
private void RecordAssignment(ILocalSymbol local, ITypeSymbol valueType) => LocalAssignments.GetOrAdd(local, _ => PooledConcurrentSet<ITypeSymbol>.GetInstance(SymbolEqualityComparer.Default)).Add(valueType);
private void RecordAssignment(IParameterSymbol parameter, ITypeSymbol valueType) => ParameterAssignments.GetOrAdd(parameter.OriginalDefinition, _ => PooledConcurrentSet<ITypeSymbol>.GetInstance(SymbolEqualityComparer.Default)).Add(valueType);
private void RecordAssignment(IMethodSymbol method, ITypeSymbol valueType) => MethodReturns.GetOrAdd(method, _ => PooledConcurrentSet<ITypeSymbol>.GetInstance(SymbolEqualityComparer.Default)).Add(valueType);
}
}
}
Expand Up @@ -190,23 +190,9 @@ void Report(ISymbol sym, ITypeSymbol fromType, PooledConcurrentSet<ITypeSymbol>
toType = toType.WithNullableAnnotation(Analyzer.Utilities.Lightup.NullableAnnotation.Annotated);
}

if (fromType.TypeKind == TypeKind.Interface)
if (!toType.DerivesFrom(fromType.OriginalDefinition))
{
if (!toType.AllInterfaces.Contains(fromType, SymbolEqualityComparer.Default))
{
// If the from type is an interface, the destination type must implement that interface
// This deals with the case of going from T to Task<T> for example.
return;
}
}
else
{
if (!toType.DerivesFrom(fromType))
{
// if the from type is a class, then the to type must be more derived than the
// class
return;
}
return;
}

if (toType.TypeKind == TypeKind.Class
Expand Down
Expand Up @@ -11,6 +11,112 @@ namespace Microsoft.NetCore.Analyzers.Performance.UnitTests
{
public static partial class UseConcreteTypeTests
{
[Fact]
public static async Task ShouldNotTrigger1()
{
const string Source = @"
#nullable enable
using System;
using System.Collections.Generic;
namespace Example
{
public class BaseType
{
}
public class Derived1 : BaseType
{
}
public class Derived2 : BaseType
{
private BaseType? Foo(int x)
{
if (x == 0) return null;
if (x == 1) return new Derived1();
return this;
}
}
}";

await TestCSAsync(Source);
}

[Fact]
public static async Task ShouldTrigger1()
{
const string Source = @"
namespace Example
{
public interface IFoo<T>
{
void Bar();
}
public class Foo<T> : IFoo<T>
{
public void Bar() { }
}
public static class Tester
{
private static void Do<T>(IFoo<T> {|#0:foo|})
{
foo.Bar();
}
private static void MakeCall()
{
Do<int>(new Foo<int>());
}
}
}";

await TestCSAsync(Source,
VerifyCS.Diagnostic(UseConcreteTypeAnalyzer.UseConcreteTypeForParameter)
.WithLocation(0)
.WithArguments("foo", "Example.IFoo<T>", "Example.Foo<int>"));
}

[Fact]
public static async Task ShouldTrigger2()
{
const string Source = @"
namespace Example
{
public interface IFoo<T>
{
void Bar();
}
public class Foo<T> : IFoo<T>
{
public void Bar() { }
}
public static class Tester
{
private static void Do<T>(IFoo<T> {|#0:foo|})
{
foo.Bar();
}
private static void MakeCall<T>()
{
Do<T>(new Foo<T>());
}
}
}";

await TestCSAsync(Source,
VerifyCS.Diagnostic(UseConcreteTypeAnalyzer.UseConcreteTypeForParameter)
.WithLocation(0)
.WithArguments("foo", "Example.IFoo<T>", "Example.Foo<T>"));
}

[Fact]
public static async Task Params()
{
Expand Down Expand Up @@ -58,7 +164,7 @@ private void Caller(IFoo ifoo)
public static async Task Conditional()
{
const string Source = @"
#nullable enable
#nullable enable
namespace Example
{
public interface IFoo
Expand Down Expand Up @@ -172,7 +278,7 @@ public class Test
public static async Task Locals()
{
const string Source = @"
#nullable enable
#nullable enable
using System;
namespace Example
Expand Down Expand Up @@ -271,7 +377,7 @@ public void Method(int x, Foo fooParam, IFoo ifooParam)
public static async Task Complex()
{
const string Source = @"
#pragma warning disable CS0619
#pragma warning disable CS0619
namespace Example
{
Expand Down Expand Up @@ -403,7 +509,7 @@ public void M(int x)
public static async Task Fields()
{
const string Source = @"
#nullable enable
#nullable enable
using System;
namespace Example
Expand Down

0 comments on commit 5fe2c65

Please sign in to comment.