зеркало из https://github.com/dotnet/sdk.git
3053 строки
112 KiB
C#
3053 строки
112 KiB
C#
// Licensed to the .NET Foundation under one or more agreements.
|
|
// The .NET Foundation licenses this file to you under the MIT license.
|
|
|
|
using System.Runtime.CompilerServices;
|
|
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CSharp;
|
|
using Microsoft.DotNet.ApiSymbolExtensions;
|
|
using Microsoft.DotNet.ApiSymbolExtensions.Filtering;
|
|
using Microsoft.DotNet.ApiSymbolExtensions.Logging;
|
|
using Microsoft.DotNet.ApiSymbolExtensions.Tests;
|
|
using Microsoft.DotNet.GenAPI.Filtering;
|
|
|
|
namespace Microsoft.DotNet.GenAPI.Tests
|
|
{
|
|
public class CSharpFileBuilderTests
|
|
{
|
|
class AllowAllFilter : ISymbolFilter
|
|
{
|
|
public bool Include(ISymbol symbol) => true;
|
|
}
|
|
|
|
private static MetadataReference[] MetadataReferences { get; } = new[] { MetadataReference.CreateFromFile(typeof(object).Assembly!.Location!) };
|
|
|
|
private static SyntaxTree GetSyntaxTree(string syntax) =>
|
|
CSharpSyntaxTree.ParseText(syntax);
|
|
|
|
private void RunTest(string original,
|
|
string expected,
|
|
bool includeInternalSymbols = true,
|
|
bool includeEffectivelyPrivateSymbols = true,
|
|
bool includeExplicitInterfaceImplementationSymbols = true,
|
|
bool allowUnsafe = false,
|
|
string excludedAttributeFile = null,
|
|
[CallerMemberName] string assemblyName = "")
|
|
{
|
|
StringWriter stringWriter = new();
|
|
|
|
// Configure symbol filters
|
|
AccessibilitySymbolFilter accessibilitySymbolFilter = new(
|
|
includeInternalSymbols,
|
|
includeEffectivelyPrivateSymbols,
|
|
includeExplicitInterfaceImplementationSymbols);
|
|
|
|
CompositeSymbolFilter symbolFilter = new CompositeSymbolFilter()
|
|
.Add(new ImplicitSymbolFilter())
|
|
.Add(accessibilitySymbolFilter);
|
|
|
|
CompositeSymbolFilter attributeDataSymbolFilter = new();
|
|
if (excludedAttributeFile is not null)
|
|
{
|
|
attributeDataSymbolFilter.Add(new DocIdSymbolFilter(new string[] { excludedAttributeFile }));
|
|
}
|
|
attributeDataSymbolFilter.Add(accessibilitySymbolFilter);
|
|
|
|
IAssemblySymbolWriter csharpFileBuilder = new CSharpFileBuilder(
|
|
new ConsoleLog(MessageImportance.Low),
|
|
symbolFilter,
|
|
attributeDataSymbolFilter,
|
|
stringWriter,
|
|
null,
|
|
false,
|
|
MetadataReferences);
|
|
|
|
using Stream assemblyStream = SymbolFactory.EmitAssemblyStreamFromSyntax(original, enableNullable: true, allowUnsafe: allowUnsafe, assemblyName: assemblyName);
|
|
AssemblySymbolLoader assemblySymbolLoader = new(resolveAssemblyReferences: true, includeInternalSymbols: includeInternalSymbols);
|
|
assemblySymbolLoader.AddReferenceSearchPaths(typeof(object).Assembly!.Location!);
|
|
assemblySymbolLoader.AddReferenceSearchPaths(typeof(DynamicAttribute).Assembly!.Location!);
|
|
IAssemblySymbol assemblySymbol = assemblySymbolLoader.LoadAssembly(assemblyName, assemblyStream);
|
|
|
|
csharpFileBuilder.WriteAssembly(assemblySymbol);
|
|
|
|
StringBuilder stringBuilder = stringWriter.GetStringBuilder();
|
|
string resultedString = stringBuilder.ToString();
|
|
|
|
stringBuilder.Remove(0, stringBuilder.Length);
|
|
|
|
SyntaxTree resultedSyntaxTree = GetSyntaxTree(resultedString);
|
|
SyntaxTree expectedSyntaxTree = GetSyntaxTree(expected);
|
|
|
|
// compare SyntaxTree and not string representation
|
|
Assert.True(resultedSyntaxTree.IsEquivalentTo(expectedSyntaxTree),
|
|
$"Expected:\n{expected}\nResulted:\n{resultedString}");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestNamespaceDeclaration()
|
|
{
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
namespace B {}
|
|
|
|
namespace C.D { public struct Bar {} }
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace A.C.D { public partial struct Bar {} }
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestClassDeclaration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public class PublicClass { }
|
|
|
|
class InternalClass { }
|
|
|
|
public sealed class PublicSealedClass { }
|
|
|
|
public partial class ProtectedPartialClass { }
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
internal partial class InternalClass { }
|
|
|
|
/// `partial` keyword is not added!
|
|
public partial class ProtectedPartialClass { }
|
|
|
|
public partial class PublicClass { }
|
|
|
|
public sealed partial class PublicSealedClass { }
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestStructDeclaration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public struct PublicStruct { }
|
|
|
|
struct InternalStruct { }
|
|
|
|
readonly struct ReadonlyStruct { }
|
|
|
|
public readonly struct PublicReadonlyStruct { }
|
|
|
|
record struct RecordStruct { }
|
|
|
|
readonly record struct ReadonlyRecordStruct { }
|
|
|
|
public ref struct PublicRefStruct { }
|
|
|
|
public readonly ref struct PublicReadonlyRefStruct { }
|
|
}
|
|
""",
|
|
// It's expected that record structs are expanded - they're effectively syntactic sugar that is not persisted in metadata
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
internal partial struct InternalStruct
|
|
{
|
|
}
|
|
|
|
public readonly ref partial struct PublicReadonlyRefStruct
|
|
{
|
|
}
|
|
|
|
public readonly partial struct PublicReadonlyStruct
|
|
{
|
|
}
|
|
|
|
public ref partial struct PublicRefStruct
|
|
{
|
|
}
|
|
|
|
public partial struct PublicStruct
|
|
{
|
|
}
|
|
|
|
internal readonly partial struct ReadonlyRecordStruct : System.IEquatable<ReadonlyRecordStruct>
|
|
{
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public readonly bool Equals(ReadonlyRecordStruct other) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public override readonly bool Equals(object obj) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public override readonly int GetHashCode() { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public static bool operator ==(ReadonlyRecordStruct left, ReadonlyRecordStruct right) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public static bool operator !=(ReadonlyRecordStruct left, ReadonlyRecordStruct right) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public override readonly string ToString() { throw null; }
|
|
}
|
|
|
|
internal readonly partial struct ReadonlyStruct
|
|
{
|
|
}
|
|
|
|
internal partial struct RecordStruct : System.IEquatable<RecordStruct>
|
|
{
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public readonly bool Equals(RecordStruct other) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public override readonly bool Equals(object obj) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public override readonly int GetHashCode() { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public static bool operator ==(RecordStruct left, RecordStruct right) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public static bool operator !=(RecordStruct left, RecordStruct right) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public override readonly string ToString() { throw null; }
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestRecordDeclaration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public record RecordClass;
|
|
public record RecordClass1(int i);
|
|
public record RecordClass2(string s, int i);
|
|
public record DerivedRecord(string s, int i, double d) : RecordClass2(s, i);
|
|
public record DerivedRecord2(string x, int i, double d) : RecordClass2(default(string)!, i);
|
|
public record DerivedRecord3(string x, int i, double d) : RecordClass2(default(string)!, i);
|
|
public record DerivedRecord4(double d) : RecordClass2(default(string)!, default);
|
|
public record DerivedRecord5() : RecordClass2(default(string)!, default);
|
|
|
|
public record RecordClassWithMethods(int i)
|
|
{
|
|
public void DoSomething() { }
|
|
public static void DoSomethingStatic() { }
|
|
}
|
|
|
|
public record RecordClassWithProperties(int i)
|
|
{
|
|
public int AddI { get; set; }
|
|
public int AddIinit { get; init; }
|
|
public int AddIro { get; }
|
|
}
|
|
public record RecordClassWithConstructors(int i)
|
|
{
|
|
public RecordClassWithConstructors() : this(1) { }
|
|
public RecordClassWithConstructors(string s) : this(int.Parse(s)) { }
|
|
}
|
|
public record RecordWithPrivateDefaultConstructor
|
|
{
|
|
private RecordWithPrivateDefaultConstructor() { }
|
|
}
|
|
public sealed record SealedRecordWithPrivateDefaultConstructor
|
|
{
|
|
private SealedRecordWithPrivateDefaultConstructor() { }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial record DerivedRecord(string s, int i, double d) : RecordClass2(default!, default)
|
|
{
|
|
}
|
|
|
|
public partial record DerivedRecord2(string x, int i, double d) : RecordClass2(default!, default)
|
|
{
|
|
}
|
|
|
|
public partial record DerivedRecord3(string x, int i, double d) : RecordClass2(default!, default)
|
|
{
|
|
}
|
|
|
|
public partial record DerivedRecord4(double d) : RecordClass2(default!, default)
|
|
{
|
|
}
|
|
|
|
public partial record DerivedRecord5() : RecordClass2(default!, default)
|
|
{
|
|
}
|
|
|
|
public partial record RecordClass()
|
|
{
|
|
}
|
|
|
|
public partial record RecordClass1(int i)
|
|
{
|
|
}
|
|
|
|
public partial record RecordClass2(string s, int i)
|
|
{
|
|
}
|
|
|
|
public partial record RecordClassWithConstructors(int i)
|
|
{
|
|
public RecordClassWithConstructors() { }
|
|
|
|
public RecordClassWithConstructors(string s) { }
|
|
}
|
|
|
|
public partial record RecordClassWithMethods(int i)
|
|
{
|
|
public void DoSomething() { }
|
|
|
|
public static void DoSomethingStatic() { }
|
|
}
|
|
|
|
public partial record RecordClassWithProperties(int i)
|
|
{
|
|
public int AddI { get { throw null; } set { } }
|
|
|
|
public int AddIinit { get { throw null; } init { } }
|
|
|
|
public int AddIro { get { throw null; } }
|
|
}
|
|
|
|
public partial record RecordWithPrivateDefaultConstructor
|
|
{
|
|
private RecordWithPrivateDefaultConstructor() { }
|
|
}
|
|
|
|
public sealed partial record SealedRecordWithPrivateDefaultConstructor
|
|
{
|
|
private SealedRecordWithPrivateDefaultConstructor() { }
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestRecordStructDeclaration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
|
|
public record struct RecordStruct;
|
|
public record struct RecordStruct1(int i);
|
|
public record struct RecordStruct2(string s, int i);
|
|
|
|
public record struct RecordStructWithMethods(int i)
|
|
{
|
|
public void DoSomething() { }
|
|
public static void DoSomethingStatic() { }
|
|
}
|
|
|
|
public record struct RecordStructWithProperties(int i)
|
|
{
|
|
public int AddI { get; set; }
|
|
public int AddIinit { get; init; }
|
|
public int AddIro { get; }
|
|
}
|
|
public record struct RecordStructWithConstructors(int i)
|
|
{
|
|
public RecordStructWithConstructors() : this(1) { }
|
|
public RecordStructWithConstructors(string s) : this(int.Parse(s)) { }
|
|
}
|
|
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial struct RecordStruct : System.IEquatable<RecordStruct>
|
|
{
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public readonly bool Equals(RecordStruct other) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public override readonly bool Equals(object obj) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public override readonly int GetHashCode() { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public static bool operator ==(RecordStruct left, RecordStruct right) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public static bool operator !=(RecordStruct left, RecordStruct right) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public override readonly string ToString() { throw null; }
|
|
}
|
|
|
|
public partial struct RecordStruct1 : System.IEquatable<RecordStruct1>
|
|
{
|
|
private int _dummyPrimitive;
|
|
public RecordStruct1(int i) { }
|
|
|
|
public int i { get { throw null; } set { } }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public readonly void Deconstruct(out int i) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public readonly bool Equals(RecordStruct1 other) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public override readonly bool Equals(object obj) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public override readonly int GetHashCode() { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public static bool operator ==(RecordStruct1 left, RecordStruct1 right) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public static bool operator !=(RecordStruct1 left, RecordStruct1 right) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public override readonly string ToString() { throw null; }
|
|
}
|
|
|
|
public partial struct RecordStruct2 : System.IEquatable<RecordStruct2>
|
|
{
|
|
private object _dummy;
|
|
private int _dummyPrimitive;
|
|
public RecordStruct2(string s, int i) { }
|
|
|
|
public int i { get { throw null; } set { } }
|
|
|
|
public string s { get { throw null; } set { } }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public readonly void Deconstruct(out string s, out int i) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public readonly bool Equals(RecordStruct2 other) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public override readonly bool Equals(object obj) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public override readonly int GetHashCode() { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public static bool operator ==(RecordStruct2 left, RecordStruct2 right) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public static bool operator !=(RecordStruct2 left, RecordStruct2 right) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public override readonly string ToString() { throw null; }
|
|
}
|
|
|
|
public partial struct RecordStructWithConstructors : System.IEquatable<RecordStructWithConstructors>
|
|
{
|
|
private int _dummyPrimitive;
|
|
public RecordStructWithConstructors() { }
|
|
|
|
public RecordStructWithConstructors(int i) { }
|
|
|
|
public RecordStructWithConstructors(string s) { }
|
|
|
|
public int i { get { throw null; } set { } }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public readonly void Deconstruct(out int i) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public readonly bool Equals(RecordStructWithConstructors other) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public override readonly bool Equals(object obj) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public override readonly int GetHashCode() { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public static bool operator ==(RecordStructWithConstructors left, RecordStructWithConstructors right) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public static bool operator !=(RecordStructWithConstructors left, RecordStructWithConstructors right) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public override readonly string ToString() { throw null; }
|
|
}
|
|
|
|
public partial struct RecordStructWithMethods : System.IEquatable<RecordStructWithMethods>
|
|
{
|
|
private int _dummyPrimitive;
|
|
public RecordStructWithMethods(int i) { }
|
|
|
|
public int i { get { throw null; } set { } }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public readonly void Deconstruct(out int i) { throw null; }
|
|
|
|
public void DoSomething() { }
|
|
|
|
public static void DoSomethingStatic() { }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public readonly bool Equals(RecordStructWithMethods other) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public override readonly bool Equals(object obj) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public override readonly int GetHashCode() { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public static bool operator ==(RecordStructWithMethods left, RecordStructWithMethods right) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public static bool operator !=(RecordStructWithMethods left, RecordStructWithMethods right) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public override readonly string ToString() { throw null; }
|
|
}
|
|
|
|
public partial struct RecordStructWithProperties : System.IEquatable<RecordStructWithProperties>
|
|
{
|
|
private int _dummyPrimitive;
|
|
public RecordStructWithProperties(int i) { }
|
|
|
|
public int AddI { get { throw null; } set { } }
|
|
|
|
public int AddIinit { get { throw null; } init { } }
|
|
|
|
public int AddIro { get { throw null; } }
|
|
|
|
public int i { get { throw null; } set { } }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public readonly void Deconstruct(out int i) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public readonly bool Equals(RecordStructWithProperties other) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public override readonly bool Equals(object obj) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public override readonly int GetHashCode() { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public static bool operator ==(RecordStructWithProperties left, RecordStructWithProperties right) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public static bool operator !=(RecordStructWithProperties left, RecordStructWithProperties right) { throw null; }
|
|
|
|
[System.Runtime.CompilerServices.CompilerGenerated]
|
|
public override readonly string ToString() { throw null; }
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestInterfaceGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public interface IPoint
|
|
{
|
|
// Property signatures:
|
|
int X { get; set; }
|
|
int Y { get; set; }
|
|
|
|
double CalculateDistance(IPoint p);
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial interface IPoint
|
|
{
|
|
// Property signatures:
|
|
int X { get; set; }
|
|
int Y { get; set; }
|
|
|
|
double CalculateDistance(IPoint p);
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestEnumGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public enum Color
|
|
{
|
|
White = 0,
|
|
Green = 100,
|
|
Blue = 200
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public enum Color
|
|
{
|
|
White = 0,
|
|
Green = 100,
|
|
Blue = 200
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestPropertyGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public class Car
|
|
{
|
|
public int? Drivers { get; }
|
|
public int Wheels { get => 4; }
|
|
public bool IsRunning { get; set; }
|
|
public bool Is4x4 { get => false; set { } }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial class Car
|
|
{
|
|
public int? Drivers { get { throw null; } }
|
|
public bool Is4x4 { get { throw null; } set { } }
|
|
public bool IsRunning { get { throw null; } set { } }
|
|
public int Wheels { get { throw null; } }
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestAbstractPropertyGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
abstract class Car
|
|
{
|
|
abstract protected int? Wheels { get; }
|
|
abstract public bool IsRunning { get; set; }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
internal abstract partial class Car
|
|
{
|
|
public abstract bool IsRunning { get; set; }
|
|
protected abstract int? Wheels { get; }
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestExplicitInterfaceImplementation()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public interface IControl
|
|
{
|
|
void Paint();
|
|
}
|
|
public interface ISurface
|
|
{
|
|
void Paint();
|
|
}
|
|
|
|
public class SampleClass : IControl, ISurface
|
|
{
|
|
public void Paint()
|
|
{
|
|
}
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial interface IControl
|
|
{
|
|
void Paint();
|
|
}
|
|
public partial interface ISurface
|
|
{
|
|
void Paint();
|
|
}
|
|
|
|
public partial class SampleClass : IControl, ISurface
|
|
{
|
|
public void Paint()
|
|
{
|
|
}
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestPartiallySpecifiedGenericClassGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public class BaseNodeMultiple<T, U> { }
|
|
|
|
public class Node4<T> : BaseNodeMultiple<T, int> { }
|
|
|
|
public class Node5<T, U> : BaseNodeMultiple<T, U> { }
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial class BaseNodeMultiple <T, U> { }
|
|
|
|
public partial class Node4 <T> : BaseNodeMultiple<T, int> { }
|
|
|
|
public partial class Node5 <T, U> : BaseNodeMultiple<T, U> { }
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestGenericClassWitConstraintsParameterGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public class SuperKeyType<K, V, U>
|
|
where U : System.IComparable<U>
|
|
where V : new()
|
|
{ }
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial class SuperKeyType <K, V, U> where V : new()
|
|
where U : System.IComparable<U>
|
|
{
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestPublicMembersGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public enum Kind
|
|
{
|
|
None = 0,
|
|
Disable = 1
|
|
}
|
|
|
|
public readonly struct Options
|
|
{
|
|
public readonly bool BoolMember = true;
|
|
public readonly Kind KindMember = Kind.Disable;
|
|
|
|
public Options(Kind kindVal)
|
|
: this(kindVal, false)
|
|
{
|
|
}
|
|
|
|
public Options(Kind kindVal, bool boolVal)
|
|
{
|
|
BoolMember = boolVal;
|
|
KindMember = kindVal;
|
|
}
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public enum Kind
|
|
{
|
|
None = 0,
|
|
Disable = 1
|
|
}
|
|
|
|
public readonly partial struct Options
|
|
{
|
|
public readonly bool BoolMember;
|
|
public readonly Kind KindMember;
|
|
public Options(Kind kindVal, bool boolVal) { }
|
|
public Options(Kind kindVal) { }
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestDelegateGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public delegate bool SyntaxReceiverCreator(int a, bool b);
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public delegate bool SyntaxReceiverCreator(int a, bool b);
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestAbstractEventGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public abstract class AbstractEvents
|
|
{
|
|
public abstract event System.EventHandler<bool> TextChanged;
|
|
}
|
|
|
|
public class Events
|
|
{
|
|
public event System.EventHandler<string> OnNewMessage { add { } remove { } }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public abstract partial class AbstractEvents
|
|
{
|
|
public abstract event System.EventHandler<bool> TextChanged;
|
|
}
|
|
|
|
public partial class Events
|
|
{
|
|
public event System.EventHandler<string> OnNewMessage { add { } remove { } }
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestCustomAttributeGeneration()
|
|
{
|
|
RunTest(original: """
|
|
using System;
|
|
using System.Diagnostics;
|
|
|
|
namespace Foo
|
|
{
|
|
public enum Animal
|
|
{
|
|
Dog = 1,
|
|
Cat,
|
|
Bird,
|
|
}
|
|
|
|
public class AnimalTypeAttribute : Attribute
|
|
{
|
|
protected Animal thePet;
|
|
|
|
public AnimalTypeAttribute(Animal pet)
|
|
{
|
|
thePet = pet;
|
|
}
|
|
|
|
public Animal Pet
|
|
{
|
|
get { return thePet; }
|
|
set { thePet = value; }
|
|
}
|
|
}
|
|
|
|
[AnimalType(Animal.Cat)]
|
|
public class Creature
|
|
{
|
|
[AnimalType(Animal.Cat)]
|
|
[Conditional("DEBUG"), Conditional("TEST1")]
|
|
public void SayHello() { }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public enum Animal
|
|
{
|
|
Dog = 1,
|
|
Cat = 2,
|
|
Bird = 3
|
|
}
|
|
|
|
public partial class AnimalTypeAttribute : System.Attribute
|
|
{
|
|
protected Animal thePet;
|
|
public AnimalTypeAttribute(Animal pet) { }
|
|
public Animal Pet { get { throw null; } set { } }
|
|
}
|
|
|
|
[AnimalType(Animal.Cat)]
|
|
public partial class Creature
|
|
{
|
|
[AnimalType(Animal.Cat)]
|
|
[System.Diagnostics.Conditional("DEBUG")]
|
|
[System.Diagnostics.Conditional("TEST1")]
|
|
public void SayHello() { }
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestFullyQualifiedNamesForDefaultEnumParameters()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public enum Animal
|
|
{
|
|
Dog = 1,
|
|
Cat = 2,
|
|
Bird = 3
|
|
}
|
|
|
|
public class AnimalProperty {
|
|
public Animal _animal;
|
|
|
|
public AnimalProperty(Animal animal = Animal.Cat)
|
|
{
|
|
_animal = animal;
|
|
}
|
|
|
|
public int Execute(int p = 42) { return p; }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public enum Animal
|
|
{
|
|
Dog = 1,
|
|
Cat = 2,
|
|
Bird = 3
|
|
}
|
|
|
|
public partial class AnimalProperty {
|
|
public Animal _animal;
|
|
|
|
public AnimalProperty(Animal animal = Animal.Cat) { }
|
|
|
|
public int Execute(int p = 42) { throw null; }
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestCustomComparisonOperatorGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public class Car : System.IEquatable<Car>
|
|
{
|
|
public bool Equals(Car? c) { return true; }
|
|
public override bool Equals(object? o) { return true; }
|
|
public override int GetHashCode() => 0;
|
|
public static bool operator ==(Car lhs, Car rhs) { return true; }
|
|
public static bool operator !=(Car lhs, Car rhs) { return false; }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial class Car : System.IEquatable<Car>
|
|
{
|
|
public bool Equals(Car? c) { throw null; }
|
|
public override bool Equals(object? o) { throw null; }
|
|
public override int GetHashCode() { throw null; }
|
|
public static bool operator ==(Car lhs, Car rhs) { throw null; }
|
|
public static bool operator !=(Car lhs, Car rhs) { throw null; }
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestNestedClassGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public class Car
|
|
{
|
|
public class Engine
|
|
{
|
|
public class Cylinder
|
|
{ }
|
|
}
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial class Car
|
|
{
|
|
public partial class Engine
|
|
{
|
|
public partial class Cylinder
|
|
{ }
|
|
}
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
[Fact]
|
|
public void TestExplicitInterfaceImplementationMethodGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public abstract class MemoryManager : System.IDisposable
|
|
{
|
|
void System.IDisposable.Dispose() { }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public abstract partial class MemoryManager : System.IDisposable
|
|
{
|
|
void System.IDisposable.Dispose() { }
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestNullabilityGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public class Bar
|
|
{
|
|
public int? AMember { get; set; }
|
|
public string? BMember { get; }
|
|
|
|
public string? Execute(string? a, int? b) { return null; }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial class Bar
|
|
{
|
|
public int? AMember { get { throw null; } set { } }
|
|
public string? BMember { get { throw null; } }
|
|
|
|
public string? Execute(string? a, int? b) { throw null; }
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestExtensionMethodsGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public static class MyExtensions
|
|
{
|
|
public static int WordCount(this string str) { return 42; }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public static partial class MyExtensions
|
|
{
|
|
public static int WordCount(this string str) { throw null; }
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestMethodsWithVariableNumberOfArgumentsGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public class Bar
|
|
{
|
|
public void Execute(params int[] list) { }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial class Bar
|
|
{
|
|
public void Execute(params int[] list) { }
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestConversionOperatorGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public readonly struct Digit
|
|
{
|
|
private readonly byte digit;
|
|
|
|
public Digit(byte digit)
|
|
{
|
|
this.digit = digit;
|
|
}
|
|
|
|
public static implicit operator byte(Digit d) => d.digit;
|
|
public static explicit operator Digit(byte b) => new Digit(b);
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public readonly partial struct Digit
|
|
{
|
|
private readonly int _dummyPrimitive;
|
|
|
|
public Digit(byte digit) { }
|
|
public static explicit operator Digit(byte b) { throw null; }
|
|
|
|
public static implicit operator byte(Digit d) { throw null; }
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestDestructorGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public class Bar
|
|
{
|
|
~Bar() {}
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial class Bar
|
|
{
|
|
~Bar() {}
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestExplicitInterfaceImplementationPropertyGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public interface IFoo
|
|
{
|
|
int FooField { get; set; }
|
|
void FooMethod();
|
|
}
|
|
|
|
public class Bar : IFoo
|
|
{
|
|
int BarField { get; set; }
|
|
int IFoo.FooField { get; set; }
|
|
|
|
void IFoo.FooMethod() { }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial class Bar : IFoo
|
|
{
|
|
int IFoo.FooField { get { throw null; } set { } }
|
|
void IFoo.FooMethod() { }
|
|
}
|
|
|
|
public partial interface IFoo
|
|
{
|
|
int FooField { get; set; }
|
|
void FooMethod();
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestAccessibilityGenerationForPropertyAccessors()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public class Bar
|
|
{
|
|
public int P { get; protected set; }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial class Bar
|
|
{
|
|
public int P { get { throw null; } protected set { } }
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestConstantFieldGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public class Bar
|
|
{
|
|
public const int CurrentEra = 0;
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial class Bar
|
|
{
|
|
public const int CurrentEra = 0;
|
|
}
|
|
}
|
|
"""
|
|
);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestTypeParameterVarianceGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public delegate void Action<in T>(T obj);
|
|
public partial interface IAsyncEnumerable<out T>
|
|
{
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public delegate void Action<in T>(T obj);
|
|
public partial interface IAsyncEnumerable<out T>
|
|
{
|
|
}
|
|
}
|
|
"""
|
|
);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestRefMembersGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public class Bar<T>
|
|
{
|
|
#pragma warning disable CS8597
|
|
public ref T GetPinnableReference() { throw null; }
|
|
public ref readonly T this[int index] { get { throw null; } }
|
|
public ref int P { get { throw null; } }
|
|
#pragma warning restore CS8597
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial class Bar<T>
|
|
{
|
|
#pragma warning disable CS8597
|
|
public ref readonly T this[int index] { get { throw null; } }
|
|
public ref int P { get { throw null; } }
|
|
public ref T GetPinnableReference() { throw null; }
|
|
#pragma warning restore CS8597
|
|
}
|
|
}
|
|
"""
|
|
);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestDefaultConstraintOnOverrideGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public abstract partial class A
|
|
{
|
|
public abstract TResult? Accept<TResult>(int a);
|
|
}
|
|
|
|
public sealed partial class B : A
|
|
{
|
|
#pragma warning disable CS8597
|
|
public override TResult? Accept<TResult>(int a) where TResult : default { throw null; }
|
|
#pragma warning restore CS8597
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public abstract partial class A
|
|
{
|
|
public abstract TResult? Accept<TResult>(int a);
|
|
}
|
|
|
|
public sealed partial class B : A
|
|
{
|
|
#pragma warning disable CS8597
|
|
public override TResult? Accept<TResult>(int a) where TResult : default { throw null; }
|
|
#pragma warning restore CS8597
|
|
}
|
|
}
|
|
"""
|
|
);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestSynthesizePrivateFieldsForValueTypes()
|
|
{
|
|
RunTest(original: """
|
|
using System;
|
|
|
|
namespace Foo
|
|
{
|
|
public struct Bar
|
|
{
|
|
#pragma warning disable 0169
|
|
private IntPtr intPtr;
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial struct Bar
|
|
{
|
|
private int _dummyPrimitive;
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestSynthesizePrivateFieldsForReferenceTypes()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public struct Bar
|
|
{
|
|
#pragma warning disable 0169
|
|
private object field;
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial struct Bar
|
|
{
|
|
private object _dummy;
|
|
private int _dummyPrimitive;
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestSynthesizePrivateFieldsForGenericTypes()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public struct Bar<T>
|
|
{
|
|
#pragma warning disable 0169
|
|
private T _field;
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial struct Bar<T>
|
|
{
|
|
private T _field;
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestSynthesizePrivateFieldsForNestedGenericTypes()
|
|
{
|
|
RunTest(original: """
|
|
using System.Collections.Generic;
|
|
|
|
namespace Foo
|
|
{
|
|
public struct Bar<T> where T : notnull
|
|
{
|
|
#pragma warning disable 0169
|
|
private Dictionary<int, List<T>> _field;
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial struct Bar<T>
|
|
{
|
|
private System.Collections.Generic.Dictionary<int, System.Collections.Generic.List<T>> _field;
|
|
private object _dummy;
|
|
private int _dummyPrimitive;
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestSynthesizePrivateFieldsAngleBrackets()
|
|
{
|
|
RunTest(original: """
|
|
using System.Collections.Generic;
|
|
|
|
namespace Foo
|
|
{
|
|
public readonly struct Bar<T> where T : notnull
|
|
{
|
|
public List<Bar<T>> Baz { get; }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public readonly partial struct Bar<T>
|
|
{
|
|
private readonly System.Collections.Generic.List<Bar<T>> _Baz_k__BackingField;
|
|
private readonly object _dummy;
|
|
private readonly int _dummyPrimitive;
|
|
public System.Collections.Generic.List<Bar<T>> Baz { get { throw null; } }
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestSynthesizePrivateFieldsForInaccessibleNestedGenericTypes()
|
|
{
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
internal class Bar<T> {}
|
|
|
|
public struct Foo<T>
|
|
{
|
|
#pragma warning disable 0169
|
|
// as the includeInternalSymbols field is set to false and the Bar<> class is internal -
|
|
// we must skip generation of the `_field` private field.
|
|
private Bar<T> _field;
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace A
|
|
{
|
|
public partial struct Foo<T>
|
|
{
|
|
private object _dummy;
|
|
private int _dummyPrimitive;
|
|
}
|
|
}
|
|
""",
|
|
includeInternalSymbols: false);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestBaseTypeWithoutExplicitDefault()
|
|
{
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
public class B
|
|
{
|
|
}
|
|
|
|
public class C : B
|
|
{
|
|
public C() {}
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace A
|
|
{
|
|
public partial class B
|
|
{
|
|
}
|
|
|
|
public partial class C : B
|
|
{
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestBaseTypeWithExplicitDefaultConstructor()
|
|
{
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
public class B
|
|
{
|
|
public B() {}
|
|
}
|
|
|
|
public class C : B
|
|
{
|
|
public C() {}
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace A
|
|
{
|
|
public partial class B
|
|
{
|
|
}
|
|
|
|
public partial class C : B
|
|
{
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestInternalParameterlessConstructors()
|
|
{
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
public class B
|
|
{
|
|
internal B() {}
|
|
}
|
|
|
|
public class C : B
|
|
{
|
|
internal C() : base() {}
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace A
|
|
{
|
|
public partial class B
|
|
{
|
|
internal B() {}
|
|
}
|
|
|
|
public partial class C : B
|
|
{
|
|
internal C() {}
|
|
}
|
|
}
|
|
""",
|
|
includeInternalSymbols: false);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestInternalParameterizedConstructors()
|
|
{
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
public class B
|
|
{
|
|
public B(int i) {}
|
|
}
|
|
|
|
public class C : B
|
|
{
|
|
internal C() : base(0) {}
|
|
}
|
|
|
|
public class D : B
|
|
{
|
|
internal D(int i) : base(i) {}
|
|
}
|
|
|
|
public class E
|
|
{
|
|
internal E(int i) {}
|
|
internal E(string s) {}
|
|
internal E(P p) {}
|
|
}
|
|
|
|
internal class P { }
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace A
|
|
{
|
|
public partial class B
|
|
{
|
|
public B(int i) {}
|
|
}
|
|
|
|
public partial class C : B
|
|
{
|
|
internal C() : base(default) {}
|
|
}
|
|
|
|
public partial class D : B
|
|
{
|
|
internal D() : base(default) {}
|
|
}
|
|
|
|
public partial class E
|
|
{
|
|
internal E() {}
|
|
}
|
|
}
|
|
""",
|
|
includeInternalSymbols: false);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestInternalParameterizedConstructorsPreserveInternals()
|
|
{
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
public class B
|
|
{
|
|
public B(int i) {}
|
|
}
|
|
|
|
public class C : B
|
|
{
|
|
internal C() : base(0) {}
|
|
}
|
|
|
|
public class D : B
|
|
{
|
|
internal D(int i) : base(i) {}
|
|
}
|
|
|
|
public class E
|
|
{
|
|
internal E(int i) {}
|
|
internal E(string s) {}
|
|
internal E(P p) {}
|
|
}
|
|
|
|
internal class P { }
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace A
|
|
{
|
|
public partial class B
|
|
{
|
|
public B(int i) { }
|
|
}
|
|
|
|
public partial class C : B
|
|
{
|
|
internal C() : base(default) { }
|
|
}
|
|
|
|
public partial class D : B
|
|
{
|
|
internal D(int i) : base(default) { }
|
|
}
|
|
|
|
public partial class E
|
|
{
|
|
internal E(P p) { }
|
|
|
|
internal E(int i) { }
|
|
|
|
internal E(string s) { }
|
|
}
|
|
|
|
internal partial class P
|
|
{
|
|
}
|
|
}
|
|
""",
|
|
includeInternalSymbols: true);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestInternalConstructorCallingProtected()
|
|
{
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
public class B
|
|
{
|
|
protected B() {}
|
|
}
|
|
|
|
public class C : B
|
|
{
|
|
internal C() {}
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace A
|
|
{
|
|
public partial class B
|
|
{
|
|
protected B() {}
|
|
}
|
|
|
|
public partial class C : B
|
|
{
|
|
internal C() {}
|
|
}
|
|
}
|
|
""",
|
|
includeInternalSymbols: false);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestBaseTypeWithoutDefaultConstructor()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public class A
|
|
{
|
|
}
|
|
|
|
public class B
|
|
{
|
|
public B(int p1, string p2, A p3) { }
|
|
}
|
|
|
|
public class C : B
|
|
{
|
|
public C() : base(1, "", new A()) {}
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial class A
|
|
{
|
|
}
|
|
|
|
public partial class B
|
|
{
|
|
public B(int p1, string p2, A p3) { }
|
|
}
|
|
|
|
public partial class C : B
|
|
{
|
|
public C() : base(default, default!, default!) {}
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestBaseTypeWithMultipleNonDefaultConstructors()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public class A
|
|
{
|
|
}
|
|
|
|
public class B
|
|
{
|
|
public B(int p1, string p2, A p3) { }
|
|
public B(int p1, string p2) { }
|
|
public B(int p1) { }
|
|
}
|
|
|
|
public class C : B
|
|
{
|
|
public C() : base(1, "", new A()) {}
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial class A
|
|
{
|
|
}
|
|
|
|
public partial class B
|
|
{
|
|
public B(int p1, string p2, A p3) { }
|
|
public B(int p1, string p2) { }
|
|
public B(int p1) { }
|
|
}
|
|
|
|
public partial class C : B
|
|
{
|
|
public C() : base(default) {}
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestBaseTypeWithAmbiguousNonDefaultConstructors()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public class A
|
|
{
|
|
public A(char c) { }
|
|
public A(int i) { }
|
|
public A(string s) { }
|
|
}
|
|
|
|
public class B : A
|
|
{
|
|
public B() : base("") {}
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial class A
|
|
{
|
|
public A(char c) { }
|
|
public A(int i) { }
|
|
public A(string s) { }
|
|
}
|
|
|
|
public partial class B : A
|
|
{
|
|
public B() : base(default(char)) {}
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact()]
|
|
public void TestBaseTypeWithAmbiguousNonDefaultConstructorsRegression31655()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public class A
|
|
{
|
|
public A(Id id, System.Collections.Generic.IEnumerable<D> deps) { }
|
|
public A(string s, V v) { }
|
|
}
|
|
|
|
public class B : A
|
|
{
|
|
public B() : base(new Id(), new D[0]) {}
|
|
}
|
|
|
|
public class D { }
|
|
|
|
public class Id { }
|
|
|
|
public class V { }
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial class A
|
|
{
|
|
public A(Id id, System.Collections.Generic.IEnumerable<D> deps) { }
|
|
public A(string s, V v) { }
|
|
}
|
|
|
|
public partial class B : A
|
|
{
|
|
public B() : base(default!, default(System.Collections.Generic.IEnumerable<D>)!) {}
|
|
}
|
|
|
|
public partial class D { }
|
|
|
|
public partial class Id { }
|
|
|
|
public partial class V { }
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestBaseTypeConstructorWithObsoleteAttribute()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public class B
|
|
{
|
|
public B(int p1, string p2) { }
|
|
[System.Obsolete("Constructor is deprecated.", true)]
|
|
public B(int p1) { }
|
|
}
|
|
|
|
public class C : B
|
|
{
|
|
public C() : base(1, "") { }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial class B
|
|
{
|
|
public B(int p1, string p2) { }
|
|
[System.Obsolete("Constructor is deprecated.", true)]
|
|
public B(int p1) { }
|
|
}
|
|
|
|
public partial class C : B
|
|
{
|
|
public C() : base(default, default!) { }
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestObsoleteBaseTypeConstructorWithoutErrorParameter()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public class B
|
|
{
|
|
public B(int p1, string p2) { }
|
|
[System.Obsolete("Constructor is deprecated.")]
|
|
public B(int p1) { }
|
|
}
|
|
|
|
public class C : B
|
|
{
|
|
public C() : base(1, "") { }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial class B
|
|
{
|
|
public B(int p1, string p2) { }
|
|
[System.Obsolete("Constructor is deprecated.")]
|
|
public B(int p1) { }
|
|
}
|
|
|
|
public partial class C : B
|
|
{
|
|
public C() : base(default) { }
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestObsoleteBaseTypeConstructorWithoutMessageParameter()
|
|
{
|
|
RunTest(original: """
|
|
namespace Foo
|
|
{
|
|
public class B
|
|
{
|
|
public B(int p1, string p2) { }
|
|
[System.Obsolete(null)]
|
|
public B(int p1) { }
|
|
}
|
|
|
|
public class C : B
|
|
{
|
|
public C() : base(1, "") { }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace Foo
|
|
{
|
|
public partial class B
|
|
{
|
|
public B(int p1, string p2) { }
|
|
[System.Obsolete(null)]
|
|
public B(int p1) { }
|
|
}
|
|
|
|
public partial class C : B
|
|
{
|
|
public C() : base(default) { }
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestFilterOutInternalExplicitInterfaceImplementation()
|
|
{
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
internal interface Foo
|
|
{
|
|
void XYZ();
|
|
int ABC { get; set; }
|
|
}
|
|
|
|
public class Bar : Foo
|
|
{
|
|
int Foo.ABC { get => 1; set { } }
|
|
void Foo.XYZ() { }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace A
|
|
{
|
|
public partial class Bar
|
|
{
|
|
}
|
|
}
|
|
""",
|
|
includeInternalSymbols: false);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestMethodsWithReferenceParameterGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
public class foo
|
|
{
|
|
public void Execute(out int i) { i = 1; }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace A
|
|
{
|
|
public partial class foo
|
|
{
|
|
public void Execute(out int i) { throw null; }
|
|
}
|
|
}
|
|
""",
|
|
includeInternalSymbols: false);
|
|
}
|
|
|
|
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/74109")]
|
|
public void TestInterfaceWithOperatorGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
public interface IntType
|
|
{
|
|
public static IntType operator +(IntType left, IntType right) => left + right;
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace A
|
|
{
|
|
public partial interface IntType
|
|
{
|
|
public static IntType operator +(IntType left, IntType right) => left + right;
|
|
}
|
|
}
|
|
""",
|
|
includeInternalSymbols: false);
|
|
}
|
|
|
|
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/74109")]
|
|
public void TestInterfaceWithCheckedOperatorGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
public interface IAdditionOperators<TSelf, TOther, TResult>
|
|
where TSelf : IAdditionOperators<TSelf, TOther, TResult>?
|
|
{
|
|
static abstract TResult operator +(TSelf left, TOther right);
|
|
static virtual TResult operator checked +(TSelf left, TOther right) => left + right;
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace A
|
|
{
|
|
public interface IAdditionOperators<TSelf, TOther, TResult>
|
|
where TSelf : IAdditionOperators<TSelf, TOther, TResult>?
|
|
{
|
|
static abstract TResult operator +(TSelf left, TOther right);
|
|
static virtual TResult operator checked +(TSelf left, TOther right) { throw null; }
|
|
}
|
|
}
|
|
""",
|
|
includeInternalSymbols: false);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestUnsafeFieldGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
public struct Node
|
|
{
|
|
public unsafe Node* Left;
|
|
public unsafe Node* Right;
|
|
public int Value;
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace A
|
|
{
|
|
public partial struct Node
|
|
{
|
|
public unsafe Node* Left;
|
|
public unsafe Node* Right;
|
|
public int Value;
|
|
}
|
|
}
|
|
""",
|
|
includeInternalSymbols: false,
|
|
allowUnsafe: true);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestUnsafeMethodGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
public unsafe class A
|
|
{
|
|
public virtual void F(char* p) {}
|
|
}
|
|
|
|
public class B: A
|
|
{
|
|
public unsafe override void F(char* p) {}
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace A
|
|
{
|
|
public partial class A
|
|
{
|
|
public virtual unsafe void F(char* p) { }
|
|
}
|
|
|
|
public partial class B : A
|
|
{
|
|
public override unsafe void F(char* p) { }
|
|
}
|
|
}
|
|
""",
|
|
includeInternalSymbols: false,
|
|
allowUnsafe: true);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestUnsafeConstructorGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
public class Bar
|
|
{
|
|
public unsafe Bar(char* f) { }
|
|
}
|
|
|
|
public class Foo : Bar
|
|
{
|
|
public unsafe Foo(char* f) : base(f) { }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace A
|
|
{
|
|
public partial class Bar
|
|
{
|
|
public unsafe Bar(char* f) { }
|
|
}
|
|
|
|
public partial class Foo : Bar
|
|
{
|
|
public unsafe Foo(char* f) : base(default) { }
|
|
}
|
|
}
|
|
""",
|
|
includeInternalSymbols: false,
|
|
allowUnsafe: true);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestUnsafeBaseConstructorGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
public class Bar
|
|
{
|
|
public unsafe Bar(char* f) { }
|
|
}
|
|
|
|
public class Foo : Bar
|
|
{
|
|
public unsafe Foo() : base(default) { }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace A
|
|
{
|
|
public partial class Bar
|
|
{
|
|
public unsafe Bar(char* f) { }
|
|
}
|
|
|
|
public partial class Foo : Bar
|
|
{
|
|
public unsafe Foo() : base(default) { }
|
|
}
|
|
}
|
|
""",
|
|
includeInternalSymbols: false,
|
|
allowUnsafe: true);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestInternalDefaultConstructorGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
public class Bar
|
|
{
|
|
public Bar(int a) { }
|
|
}
|
|
|
|
public class Foo : Bar
|
|
{
|
|
internal Foo() : base(1) { }
|
|
}
|
|
}
|
|
namespace B
|
|
{
|
|
public class Bar
|
|
{
|
|
public Bar(int a) { }
|
|
}
|
|
|
|
public class Foo : Bar
|
|
{
|
|
private Foo() : base(1) { }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace A
|
|
{
|
|
public partial class Bar
|
|
{
|
|
public Bar(int a) { }
|
|
}
|
|
|
|
public partial class Foo : Bar
|
|
{
|
|
internal Foo() : base(default) { }
|
|
}
|
|
}
|
|
namespace B
|
|
{
|
|
public partial class Bar
|
|
{
|
|
public Bar(int a) { }
|
|
}
|
|
|
|
public partial class Foo : Bar
|
|
{
|
|
internal Foo() : base(default) { }
|
|
}
|
|
}
|
|
""",
|
|
includeInternalSymbols: false);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestPrivateDefaultConstructorGeneration()
|
|
{
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
public class Bar
|
|
{
|
|
public Bar(int a) { }
|
|
}
|
|
|
|
public class Foo : Bar
|
|
{
|
|
private Foo() : base(1) { }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace A
|
|
{
|
|
public partial class Bar
|
|
{
|
|
public Bar(int a) { }
|
|
}
|
|
|
|
public partial class Foo : Bar
|
|
{
|
|
private Foo() : base(default) { }
|
|
}
|
|
}
|
|
""",
|
|
includeInternalSymbols: true,
|
|
includeEffectivelyPrivateSymbols: true);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestInternalDefaultConstructorGenerationForGenericType()
|
|
{
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
public class Bar
|
|
{
|
|
public Bar(int a) { }
|
|
}
|
|
|
|
public class Foo<T> : Bar
|
|
{
|
|
internal Foo() : base(1) { }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace A
|
|
{
|
|
public partial class Bar
|
|
{
|
|
public Bar(int a) { }
|
|
}
|
|
|
|
public partial class Foo<T> : Bar
|
|
{
|
|
internal Foo() : base(default) { }
|
|
}
|
|
}
|
|
""",
|
|
includeInternalSymbols: false);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestExplicitParameterlessConstructorNotRemoved()
|
|
{
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
public class Bar
|
|
{
|
|
public Bar() { }
|
|
public Bar(int a) { }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace A
|
|
{
|
|
public partial class Bar
|
|
{
|
|
public Bar() { }
|
|
public Bar(int a) { }
|
|
}
|
|
}
|
|
""",
|
|
includeInternalSymbols: false);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestBaseClassWithExplicitDefaultConstructor()
|
|
{
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
public class Bar
|
|
{
|
|
public Bar() { }
|
|
}
|
|
|
|
public class Foo : Bar
|
|
{
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace A
|
|
{
|
|
public partial class Bar
|
|
{
|
|
}
|
|
|
|
public partial class Foo : Bar
|
|
{
|
|
}
|
|
}
|
|
""",
|
|
includeInternalSymbols: false);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestGenericBaseInterfaceWithInaccessibleTypeArguments()
|
|
{
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
internal class IOption2
|
|
{
|
|
}
|
|
|
|
public interface IOption
|
|
{
|
|
}
|
|
|
|
public interface AreEqual<T>
|
|
{
|
|
bool Compare(T other);
|
|
}
|
|
|
|
public class PerLanguageOption : AreEqual<IOption2>, IOption
|
|
{
|
|
bool AreEqual<IOption2>.Compare(IOption2 other) => false;
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace A
|
|
{
|
|
public partial interface AreEqual<T>
|
|
{
|
|
bool Compare(T other);
|
|
}
|
|
|
|
public partial interface IOption
|
|
{
|
|
}
|
|
|
|
public partial class PerLanguageOption : IOption
|
|
{
|
|
}
|
|
}
|
|
""",
|
|
includeInternalSymbols: false);
|
|
}
|
|
|
|
[Fact]
|
|
public void NewKeywordWhenBaseMethodIsHidden()
|
|
{
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
using System;
|
|
public partial class C : IFun, IExplicit, IExplicit2 {
|
|
public int Foo;
|
|
public const int Bar = 29;
|
|
public int Baz { get; }
|
|
public int ExplicitProperty { get; }
|
|
#pragma warning disable 8618
|
|
public event EventHandler MyEvent;
|
|
void IExplicit.Explicit() {}
|
|
#pragma warning disable 8625
|
|
public void Do() => MyEvent(default(object), default(EventArgs));
|
|
public void Do(float f) {}
|
|
public static void DoStatic() {}
|
|
public void Explicit2() {}
|
|
public void Fun() {}
|
|
public void Gen<T>() {}
|
|
public void Zoo() {}
|
|
public class MyNestedClass {}
|
|
public struct MyNestedStruct {}
|
|
public class MyNestedGenericClass<T> {}
|
|
public struct MyNestedGenericStruct<T> {}
|
|
public C this[int i]
|
|
{
|
|
get => default(C)!;
|
|
set {}
|
|
}
|
|
}
|
|
public class D : C, IExplicit, IExplicit2 {
|
|
public new int Foo;
|
|
public new const int Bar = 30;
|
|
int IExplicit2.ExplicitProperty { get; }
|
|
public new int Baz { get; set; }
|
|
public new event EventHandler MyEvent;
|
|
void IExplicit2.Explicit2() {}
|
|
public new void Do() => MyEvent(default(object), default(EventArgs));
|
|
public void Do(int i) {}
|
|
public new static void DoStatic() {}
|
|
public void Explicit() {}
|
|
public new void Fun() {}
|
|
public new void Gen<T>() where T : IComparable {}
|
|
public new class MyNestedClass {}
|
|
public new struct MyNestedStruct {}
|
|
public new class MyNestedGenericClass<T> {}
|
|
public new struct MyNestedGenericStruct<T> {}
|
|
public new D this[int i]
|
|
{
|
|
get => default(D)!;
|
|
set {}
|
|
}
|
|
}
|
|
public class E : C, IExplicit, IExplicit2 {
|
|
public new int Bar;
|
|
public new const int Do = 30;
|
|
int IExplicit2.ExplicitProperty { get; }
|
|
public new int Foo { get; set; }
|
|
public new event EventHandler MyNestedClass;
|
|
void IExplicit.Explicit() {}
|
|
void IExplicit2.Explicit2() {}
|
|
public new void Baz() => MyNestedClass(default(object), default(EventArgs));
|
|
public new void MyNestedStruct(double d) {}
|
|
public new void Zoo() {}
|
|
}
|
|
public interface IExplicit {
|
|
void Explicit();
|
|
}
|
|
public interface IExplicit2 {
|
|
int ExplicitProperty { get; }
|
|
void Explicit2();
|
|
}
|
|
public interface IFun {
|
|
void Fun();
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace A
|
|
{
|
|
public partial class C : IFun, IExplicit, IExplicit2
|
|
{
|
|
public const int Bar = 29;
|
|
public int Foo;
|
|
public int Baz { get { throw null; } }
|
|
public int ExplicitProperty { get { throw null; } }
|
|
public C this[int i] { get { throw null; } set {} }
|
|
public event System.EventHandler MyEvent { add {} remove {} }
|
|
void IExplicit.Explicit() {}
|
|
public void Do() {}
|
|
public void Do(float f) {}
|
|
public static void DoStatic() {}
|
|
public void Explicit2() {}
|
|
public void Fun() {}
|
|
public void Gen<T>() {}
|
|
public void Zoo() {}
|
|
public partial class MyNestedClass {}
|
|
public partial class MyNestedGenericClass<T> {}
|
|
public partial struct MyNestedGenericStruct<T> {}
|
|
public partial struct MyNestedStruct {}
|
|
}
|
|
public partial class D : C, IExplicit, IExplicit2
|
|
{
|
|
public new const int Bar = 30;
|
|
public new int Foo;
|
|
int IExplicit2.ExplicitProperty { get { throw null; } }
|
|
public new int Baz { get { throw null; } set {} }
|
|
public new D this[int i] { get { throw null; } set {} }
|
|
public new event System.EventHandler MyEvent { add {} remove {} }
|
|
void IExplicit2.Explicit2() {}
|
|
public new void Do() {}
|
|
public void Do(int i) {}
|
|
public new static void DoStatic() {}
|
|
public void Explicit() {}
|
|
public new void Fun() {}
|
|
public new void Gen<T>() where T : System.IComparable {}
|
|
public new partial class MyNestedClass {}
|
|
public new partial class MyNestedGenericClass<T> {}
|
|
public new partial struct MyNestedGenericStruct<T> {}
|
|
public new partial struct MyNestedStruct {}
|
|
}
|
|
public partial class E : C, IExplicit, IExplicit2
|
|
{
|
|
public new int Bar;
|
|
public new const int Do = 30;
|
|
int IExplicit2.ExplicitProperty { get { throw null; } }
|
|
public new int Foo { get { throw null; } set {} }
|
|
public new event System.EventHandler MyNestedClass { add {} remove {} }
|
|
void IExplicit.Explicit() {}
|
|
void IExplicit2.Explicit2() {}
|
|
public new void Baz() {}
|
|
public new void MyNestedStruct(double d) {}
|
|
public new void Zoo() {}
|
|
}
|
|
public partial interface IExplicit {
|
|
void Explicit();
|
|
}
|
|
public partial interface IExplicit2 {
|
|
int ExplicitProperty { get; }
|
|
void Explicit2();
|
|
}
|
|
public partial interface IFun {
|
|
void Fun();
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(true)]
|
|
[InlineData(false)]
|
|
public void TestAttributeWithInternalTypeArgumentOmitted(bool includeInternalSymbols)
|
|
{
|
|
string expected = includeInternalSymbols ? """
|
|
namespace A
|
|
{
|
|
public partial class AnyTestAttribute : System.Attribute
|
|
{
|
|
public AnyTestAttribute(System.Type xType) { }
|
|
|
|
public System.Type XType { get { throw null; } set { } }
|
|
}
|
|
|
|
internal partial class InternalClass
|
|
{
|
|
}
|
|
|
|
[AnyTest(typeof(InternalClass))]
|
|
[System.Obsolete]
|
|
public partial class PublicClass
|
|
{
|
|
}
|
|
}
|
|
""" : """
|
|
namespace A
|
|
{
|
|
public partial class AnyTestAttribute : System.Attribute
|
|
{
|
|
public AnyTestAttribute(System.Type xType) { }
|
|
|
|
public System.Type XType { get { throw null; } set { } }
|
|
}
|
|
|
|
[System.Obsolete]
|
|
public partial class PublicClass
|
|
{
|
|
}
|
|
}
|
|
""";
|
|
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
internal class InternalClass { }
|
|
public partial class AnyTestAttribute : System.Attribute
|
|
{
|
|
public AnyTestAttribute(System.Type xType)
|
|
{
|
|
XType = xType;
|
|
}
|
|
|
|
public System.Type XType { get; set; }
|
|
}
|
|
|
|
[AnyTest(typeof(InternalClass))]
|
|
[System.Obsolete]
|
|
public class PublicClass { }
|
|
}
|
|
""",
|
|
expected: expected,
|
|
includeInternalSymbols: includeInternalSymbols);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestAttributesExcludedWithFilter()
|
|
{
|
|
using TempDirectory root = new();
|
|
string filePath = Path.Combine(root.DirPath, "exclusions.txt");
|
|
File.WriteAllText(filePath, "T:A.AnyTestAttribute");
|
|
|
|
RunTest(original: """
|
|
namespace A
|
|
{
|
|
public partial class AnyTestAttribute : System.Attribute
|
|
{
|
|
public AnyTestAttribute(System.Type xType)
|
|
{
|
|
XType = xType;
|
|
}
|
|
|
|
public System.Type XType { get; set; }
|
|
}
|
|
|
|
[AnyTest(typeof(string))]
|
|
[System.Obsolete]
|
|
public class PublicClass { }
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace A
|
|
{
|
|
public partial class AnyTestAttribute : System.Attribute
|
|
{
|
|
public AnyTestAttribute(System.Type xType) { }
|
|
|
|
public System.Type XType { get { throw null; } set { } }
|
|
}
|
|
|
|
[System.Obsolete]
|
|
public partial class PublicClass
|
|
{
|
|
}
|
|
}
|
|
""",
|
|
includeInternalSymbols: false,
|
|
excludedAttributeFile: filePath);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestGenericClassImplementsGenericInterface()
|
|
{
|
|
RunTest(original: """
|
|
using System;
|
|
namespace A
|
|
{
|
|
public class Foo<T> : System.Collections.ICollection, System.Collections.Generic.ICollection<T>
|
|
{
|
|
int System.Collections.Generic.ICollection<T>.Count => throw new NotImplementedException();
|
|
bool System.Collections.Generic.ICollection<T>.IsReadOnly => throw new NotImplementedException();
|
|
int System.Collections.ICollection.Count => throw new NotImplementedException();
|
|
bool System.Collections.ICollection.IsSynchronized => throw new NotImplementedException();
|
|
object System.Collections.ICollection.SyncRoot => throw new NotImplementedException();
|
|
void System.Collections.Generic.ICollection<T>.Add(T item) => throw new NotImplementedException();
|
|
void System.Collections.Generic.ICollection<T>.Clear() => throw new NotImplementedException();
|
|
bool System.Collections.Generic.ICollection<T>.Contains(T item) => throw new NotImplementedException();
|
|
void System.Collections.Generic.ICollection<T>.CopyTo(T[] array, int arrayIndex) => throw new NotImplementedException();
|
|
bool System.Collections.Generic.ICollection<T>.Remove(T item) => throw new NotImplementedException();
|
|
System.Collections.Generic.IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator() => throw new NotImplementedException();
|
|
void System.Collections.ICollection.CopyTo(System.Array array, int index) => throw new NotImplementedException();
|
|
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw new NotImplementedException();
|
|
|
|
}
|
|
}
|
|
|
|
""",
|
|
// https://github.com/dotnet/sdk/issues/32195 tracks interface expansion
|
|
expected: """
|
|
namespace A
|
|
{
|
|
public partial class Foo<T> : System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.Generic.ICollection<T>, System.Collections.Generic.IEnumerable<T>
|
|
{
|
|
int System.Collections.Generic.ICollection<T>.Count { get { throw null; } }
|
|
bool System.Collections.Generic.ICollection<T>.IsReadOnly { get { throw null; } }
|
|
int System.Collections.ICollection.Count { get { throw null; } }
|
|
bool System.Collections.ICollection.IsSynchronized { get { throw null; } }
|
|
object System.Collections.ICollection.SyncRoot { get { throw null; } }
|
|
void System.Collections.Generic.ICollection<T>.Add(T item) { }
|
|
void System.Collections.Generic.ICollection<T>.Clear() { }
|
|
bool System.Collections.Generic.ICollection<T>.Contains(T item) { throw null; }
|
|
void System.Collections.Generic.ICollection<T>.CopyTo(T[] array, int arrayIndex) { }
|
|
bool System.Collections.Generic.ICollection<T>.Remove(T item) { throw null; }
|
|
System.Collections.Generic.IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator() { throw null; }
|
|
void System.Collections.ICollection.CopyTo(System.Array array, int index) { }
|
|
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
|
|
}
|
|
}
|
|
""",
|
|
includeInternalSymbols: false);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestTypeForwardsToGenericTypesRegression31250()
|
|
{
|
|
RunTest(original: """
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Action))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Action<,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Action<,,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Action<,,,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Action<,,,,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Action<,,,,,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Action<,,,,,,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Action<,,,,,,,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Action<,,,,,,,,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.ValueTuple))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.ValueTuple<>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.ValueTuple<,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.ValueTuple<,,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.ValueTuple<,,,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.ValueTuple<,,,,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.ValueTuple<,,,,,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.ValueTuple<,,,,,,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.ValueTuple<,,,,,,,>))]
|
|
""",
|
|
expected: """
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Action))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Action<,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Action<,,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Action<,,,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Action<,,,,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Action<,,,,,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Action<,,,,,,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Action<,,,,,,,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Action<,,,,,,,,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.ValueTuple))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.ValueTuple<>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.ValueTuple<,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.ValueTuple<,,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.ValueTuple<,,,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.ValueTuple<,,,,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.ValueTuple<,,,,,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.ValueTuple<,,,,,,>))]
|
|
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.ValueTuple<,,,,,,,>))]
|
|
""",
|
|
includeInternalSymbols: false);
|
|
}
|
|
|
|
[Fact]
|
|
public void ReservedAttributesAreOmitted()
|
|
{
|
|
RunTest(original: """
|
|
namespace N {
|
|
public ref struct C<T>
|
|
where T : unmanaged
|
|
{
|
|
public required (string? k, dynamic v, nint n) X { get; init; }
|
|
}
|
|
|
|
public static class E
|
|
{
|
|
public static void M<T>(this object c, scoped System.ReadOnlySpan<T> values) { }
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace N
|
|
{
|
|
public ref partial struct C<T>
|
|
where T : unmanaged
|
|
{
|
|
public required (string? k, dynamic v, nint n) X { get { throw null; } init { } }
|
|
}
|
|
|
|
public static partial class E
|
|
{
|
|
public static void M<T>(this object c, scoped System.ReadOnlySpan<T> values) { }
|
|
}
|
|
}
|
|
""");
|
|
}
|
|
|
|
[Fact]
|
|
public void TestExplicitInterfaceIndexer()
|
|
{
|
|
RunTest(original: """
|
|
namespace a
|
|
{
|
|
public interface IFooList
|
|
{
|
|
object this[int index] { get; set; }
|
|
}
|
|
|
|
public struct Bar : IFooList
|
|
{
|
|
#pragma warning disable CS8597
|
|
public string this[int index] { get { throw null; } set { } }
|
|
object IFooList.this[int index] { get { throw null; } set { } }
|
|
#pragma warning restore CS8597
|
|
}
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace a
|
|
{
|
|
public partial struct Bar : IFooList
|
|
{
|
|
object IFooList.this[int index] { get { throw null; } set { } }
|
|
public string this[int index] { get { throw null; } set { } }
|
|
}
|
|
|
|
public partial interface IFooList
|
|
{
|
|
object this[int index] { get; set; }
|
|
}
|
|
}
|
|
""",
|
|
includeInternalSymbols: false);
|
|
}
|
|
|
|
[Fact]
|
|
public void TestExplicitInterfaceNonGenericCollections()
|
|
{
|
|
RunTest(original: """
|
|
using System;
|
|
using System.Collections;
|
|
namespace a
|
|
{
|
|
#pragma warning disable CS8597
|
|
|
|
public partial class MyStringCollection : ICollection, IEnumerable, IList
|
|
{
|
|
public int Count { get { throw null; } }
|
|
public string this[int index] { get { throw null; } set { } }
|
|
bool ICollection.IsSynchronized { get { throw null; } }
|
|
object ICollection.SyncRoot { get { throw null; } }
|
|
bool IList.IsFixedSize { get { throw null; } }
|
|
bool IList.IsReadOnly { get { throw null; } }
|
|
object? IList.this[int index] { get { throw null; } set { } }
|
|
public int Add(string value) { throw null; }
|
|
public void AddRange(string[] value) { }
|
|
public void AddRange(MyStringCollection value) { }
|
|
public void Clear() { }
|
|
public bool Contains(string value) { throw null; }
|
|
public void CopyTo(string[] array, int index) { }
|
|
public override int GetHashCode() { throw null; }
|
|
public int IndexOf(string value) { throw null; }
|
|
public void Insert(int index, string value) { }
|
|
public void Remove(string value) { }
|
|
public void RemoveAt(int index) { }
|
|
void ICollection.CopyTo(Array array, int index) { }
|
|
IEnumerator IEnumerable.GetEnumerator() { throw null; }
|
|
int IList.Add(object? value) { throw null; }
|
|
bool IList.Contains(object? value) { throw null; }
|
|
int IList.IndexOf(object? value) { throw null; }
|
|
void IList.Insert(int index, object? value) { }
|
|
void IList.Remove(object? value) { }
|
|
}
|
|
|
|
#pragma warning restore CS8597
|
|
}
|
|
""",
|
|
expected: """
|
|
namespace a
|
|
{
|
|
public partial class MyStringCollection : System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList
|
|
{
|
|
public int Count { get { throw null; } }
|
|
public string this[int index] { get { throw null; } set { } }
|
|
bool System.Collections.ICollection.IsSynchronized { get { throw null; } }
|
|
object System.Collections.ICollection.SyncRoot { get { throw null; } }
|
|
bool System.Collections.IList.IsFixedSize { get { throw null; } }
|
|
bool System.Collections.IList.IsReadOnly { get { throw null; } }
|
|
object? System.Collections.IList.this[int index] { get { throw null; } set { } }
|
|
public int Add(string value) { throw null; }
|
|
public void AddRange(MyStringCollection value) { }
|
|
public void AddRange(string[] value) { }
|
|
public void Clear() { }
|
|
public bool Contains(string value) { throw null; }
|
|
public void CopyTo(string[] array, int index) { }
|
|
public override int GetHashCode() { throw null; }
|
|
public int IndexOf(string value) { throw null; }
|
|
public void Insert(int index, string value) { }
|
|
public void Remove(string value) { }
|
|
public void RemoveAt(int index) { }
|
|
void System.Collections.ICollection.CopyTo(System.Array array, int index) { }
|
|
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
|
|
int System.Collections.IList.Add(object? value) { throw null; }
|
|
bool System.Collections.IList.Contains(object? value) { throw null; }
|
|
int System.Collections.IList.IndexOf(object? value) { throw null; }
|
|
void System.Collections.IList.Insert(int index, object? value) { }
|
|
void System.Collections.IList.Remove(object? value) { }
|
|
}
|
|
}
|
|
""",
|
|
includeInternalSymbols: false);
|
|
}
|
|
}
|
|
}
|