Skip to content

Commit

Permalink
[GH-144] - handling protected internal constructors when analyzing NS…
Browse files Browse the repository at this point in the history
…2001 diagnostic
  • Loading branch information
tpodolak committed Sep 6, 2020
1 parent d31a559 commit 536d4ad
Show file tree
Hide file tree
Showing 15 changed files with 981 additions and 16 deletions.
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"sdk": {
"version": "2.2.104"
"version": "3.1.201"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,27 @@ private IMethodSymbol[] GetAccessibleConstructors(ITypeSymbol genericArgument)
{
var internalsVisibleToProxy = genericArgument.InternalsVisibleToProxyGenerator();

bool IsAccessible(IMethodSymbol symbol)
{
return symbol.DeclaredAccessibility == Accessibility.Protected ||
symbol.DeclaredAccessibility == Accessibility.Public;
}

bool IsVisibleToProxy(IMethodSymbol symbol)
{
if (internalsVisibleToProxy == false)
{
return false;
}

return symbol.DeclaredAccessibility == Accessibility.Internal ||
symbol.DeclaredAccessibility == Accessibility.ProtectedOrInternal;
}

return genericArgument.GetMembers().OfType<IMethodSymbol>().Where(symbol =>
symbol.MethodKind == MethodKind.Constructor &&
symbol.IsStatic == false &&
(symbol.DeclaredAccessibility == Accessibility.Protected ||
symbol.DeclaredAccessibility == Accessibility.Public ||
(internalsVisibleToProxy && symbol.DeclaredAccessibility == Accessibility.Internal))).ToArray();
(IsAccessible(symbol) || IsVisibleToProxy(symbol))).ToArray();
}

private TypeInfo GetTypeInfo(SubstituteContext<TInvocationExpression> substituteContext, SyntaxNode syntax)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,106 @@ public void Test()
await VerifyDiagnostic(source, SubstituteForWithoutAccessibleConstructorDescriptor, "Could not find accessible constructor. Make sure that type MyNamespace.Foo exposes public or protected constructors.");
}

public override async Task ReturnsDiagnostic_WhenUsedForClassWithInternalConstructor_AndInternalsVisibleToNotApplied()
{
var source = @"using NSubstitute;
namespace MyNamespace
{
public class Foo
{
internal Foo()
{
}
}
public class FooTests
{
public void Test()
{
var substitute = [|NSubstitute.Substitute.For<Foo>()|];
}
}
}";
await VerifyDiagnostic(source, SubstituteForWithoutAccessibleConstructorDescriptor, "Could not find accessible constructor. Make sure that type MyNamespace.Foo exposes public or protected constructors.");
}

public override async Task ReturnsDiagnostic_WhenUsedForClassWithProtectedInternalConstructor_AndInternalsVisibleToNotApplied()
{
var source = @"using NSubstitute;
namespace MyNamespace
{
public class Foo
{
protected internal Foo()
{
}
}
public class FooTests
{
public void Test()
{
var substitute = [|NSubstitute.Substitute.For<Foo>()|];
}
}
}";
await VerifyDiagnostic(source, SubstituteForWithoutAccessibleConstructorDescriptor, "Could not find accessible constructor. Make sure that type MyNamespace.Foo exposes public or protected constructors.");
}

public override async Task ReturnsNoDiagnostic_WhenUsedForClassWithInternalConstructor_AndInternalsVisibleToApplied()
{
var source = @"using NSubstitute;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo(""DynamicProxyGenAssembly2"")]
namespace MyNamespace
{
public class Foo
{
internal Foo()
{
}
}
public class FooTests
{
public void Test()
{
var substitute = NSubstitute.Substitute.For<Foo>();
}
}
}";
await VerifyNoDiagnostic(source);
}

public override async Task ReturnsNoDiagnostic_WhenUsedForClassWithProtectedInternalConstructor_AndInternalsVisibleToApplied()
{
var source = @"using NSubstitute;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo(""DynamicProxyGenAssembly2"")]
namespace MyNamespace
{
public class Foo
{
protected internal Foo()
{
}
}
public class FooTests
{
public void Test()
{
var substitute = NSubstitute.Substitute.For<Foo>();
}
}
}";
await VerifyNoDiagnostic(source);
}

[Fact]
public override async Task ReturnsDiagnostic_WhenPassedParametersCount_GreaterThanCtorParametersCount()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,106 @@ public void Test()
await VerifyDiagnostic(source, SubstituteForWithoutAccessibleConstructorDescriptor, "Could not find accessible constructor. Make sure that type MyNamespace.Foo exposes public or protected constructors.");
}

public override async Task ReturnsDiagnostic_WhenUsedForClassWithInternalConstructor_AndInternalsVisibleToNotApplied()
{
var source = @"using NSubstitute;
namespace MyNamespace
{
public class Foo
{
internal Foo()
{
}
}
public class FooTests
{
public void Test()
{
var substitute = [|NSubstitute.Substitute.For(new [] { typeof(Foo) }, null)|];
}
}
}";
await VerifyDiagnostic(source, SubstituteForWithoutAccessibleConstructorDescriptor, "Could not find accessible constructor. Make sure that type MyNamespace.Foo exposes public or protected constructors.");
}

public override async Task ReturnsDiagnostic_WhenUsedForClassWithProtectedInternalConstructor_AndInternalsVisibleToNotApplied()
{
var source = @"using NSubstitute;
namespace MyNamespace
{
public class Foo
{
protected internal Foo()
{
}
}
public class FooTests
{
public void Test()
{
var substitute = [|NSubstitute.Substitute.For(new [] { typeof(Foo) }, null)|];
}
}
}";
await VerifyDiagnostic(source, SubstituteForWithoutAccessibleConstructorDescriptor, "Could not find accessible constructor. Make sure that type MyNamespace.Foo exposes public or protected constructors.");
}

public override async Task ReturnsNoDiagnostic_WhenUsedForClassWithInternalConstructor_AndInternalsVisibleToApplied()
{
var source = @"using NSubstitute;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo(""DynamicProxyGenAssembly2"")]
namespace MyNamespace
{
public class Foo
{
internal Foo()
{
}
}
public class FooTests
{
public void Test()
{
var substitute = NSubstitute.Substitute.For(new [] { typeof(Foo) }, null);
}
}
}";
await VerifyNoDiagnostic(source);
}

public override async Task ReturnsNoDiagnostic_WhenUsedForClassWithProtectedInternalConstructor_AndInternalsVisibleToApplied()
{
var source = @"using NSubstitute;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo(""DynamicProxyGenAssembly2"")]
namespace MyNamespace
{
public class Foo
{
protected internal Foo()
{
}
}
public class FooTests
{
public void Test()
{
var substitute = NSubstitute.Substitute.For(new [] { typeof(Foo) }, null);
}
}
}";
await VerifyNoDiagnostic(source);
}

[Fact]
public override async Task ReturnsDiagnostic_WhenPassedParametersCount_GreaterThanCtorParametersCount()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,108 @@ public void Test()
await VerifyDiagnostic(source, SubstituteForWithoutAccessibleConstructorDescriptor, "Could not find accessible constructor. Make sure that type MyNamespace.Foo exposes public or protected constructors.");
}

public override async Task ReturnsDiagnostic_WhenUsedForClassWithInternalConstructor_AndInternalsVisibleToNotApplied()
{
var source = @"using NSubstitute;
namespace MyNamespace
{
public class Foo
{
internal Foo()
{
}
}
public class FooTests
{
public void Test()
{
var substitute = [|NSubstitute.Substitute.ForPartsOf<Foo>()|];
}
}
}";
await VerifyDiagnostic(source, SubstituteForWithoutAccessibleConstructorDescriptor, "Could not find accessible constructor. Make sure that type MyNamespace.Foo exposes public or protected constructors.");
}

public override async Task ReturnsDiagnostic_WhenUsedForClassWithProtectedInternalConstructor_AndInternalsVisibleToNotApplied()
{
var source = @"using NSubstitute;
namespace MyNamespace
{
public class Foo
{
protected internal Foo()
{
}
}
public class FooTests
{
public void Test()
{
var substitute = [|NSubstitute.Substitute.ForPartsOf<Foo>()|];
}
}
}";
await VerifyDiagnostic(source, SubstituteForWithoutAccessibleConstructorDescriptor, "Could not find accessible constructor. Make sure that type MyNamespace.Foo exposes public or protected constructors.");
}

public override async Task ReturnsNoDiagnostic_WhenUsedForClassWithInternalConstructor_AndInternalsVisibleToApplied()
{
var source = @"using NSubstitute;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo(""DynamicProxyGenAssembly2"")]
namespace MyNamespace
{
public class Foo
{
internal Foo()
{
}
}
public class FooTests
{
public void Test()
{
var substitute = NSubstitute.Substitute.ForPartsOf<Foo>();
}
}
}";
await VerifyNoDiagnostic(source);
}

public override async Task ReturnsNoDiagnostic_WhenUsedForClassWithProtectedInternalConstructor_AndInternalsVisibleToApplied()
{
var source = @"using NSubstitute;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo(""DynamicProxyGenAssembly2"")]
namespace MyNamespace
{
public class Foo
{
protected internal Foo()
{
}
}
public class FooTests
{
public void Test()
{
var substitute = NSubstitute.Substitute.ForPartsOf<Foo>();
}
}
}";
await VerifyNoDiagnostic(source);
}

[Fact]
public override async Task ReturnsDiagnostic_WhenPassedParametersCount_GreaterThanCtorParametersCount()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ public abstract class SubstituteDiagnosticVerifier : CSharpDiagnosticVerifier, I
[Fact]
public abstract Task ReturnsDiagnostic_WhenUsedForClassWithoutPublicOrProtectedConstructor();

[Fact]
public abstract Task ReturnsDiagnostic_WhenUsedForClassWithInternalConstructor_AndInternalsVisibleToNotApplied();

[Fact]
public abstract Task ReturnsDiagnostic_WhenUsedForClassWithProtectedInternalConstructor_AndInternalsVisibleToNotApplied();

[Fact]
public abstract Task ReturnsNoDiagnostic_WhenUsedForClassWithInternalConstructor_AndInternalsVisibleToApplied();

[Fact]
public abstract Task ReturnsNoDiagnostic_WhenUsedForClassWithProtectedInternalConstructor_AndInternalsVisibleToApplied();

[Fact]
public abstract Task ReturnsDiagnostic_WhenPassedParametersCount_GreaterThanCtorParametersCount();

Expand Down
Loading

0 comments on commit 536d4ad

Please sign in to comment.