Skip to content
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
32 changes: 16 additions & 16 deletions docs/features/source-generators.cookbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,26 +115,26 @@ namespace GeneratedNamespace
```csharp
[Generator]
public class FileTransformGenerator : ISourceGenerator
{
public void Initialize(InitializationContext context) {}
{
public void Initialize(InitializationContext context) {}

public void Execute(SourceGeneratorContext context)
public void Execute(SourceGeneratorContext context)
{
// find anything that matches our files
var myFiles = context.AnalyzerOptions.AdditionalFiles.Where(at => at.Path.EndsWith(".xml"));
foreach (var file in myFiles)
{
// find anything that matches our files
var myFiles = context.AnalyzerOptions.AdditionalFiles.Where(at => at.Path.EndsWith(".xml"));
foreach (var file in myFiles)
{
var content = file.GetText(context.CancellationToken);
var content = file.GetText(context.CancellationToken);

// do some transforms based on the file context
string output = MyXmlToCSharpCompiler.Compile(content);
// do some transforms based on the file context
string output = MyXmlToCSharpCompiler.Compile(content);

var sourceText = SourceText.From(output, Encoding.UTF8);
var sourceText = SourceText.From(output, Encoding.UTF8);

context.AddSource($"{file.Name}generated.cs", sourceText);
}
context.AddSource($"{file.Name}generated.cs", sourceText);
}
}
}
```

### Augment user code
Expand Down Expand Up @@ -628,7 +628,7 @@ partial class MyRecord
}
```

This attribute could also be used for #participate-in-the-ide-experience,
This attribute could also be used for [Participate in the IDE experience](#participate-in-the-ide-experience),
when the full scope of that feature is fully designed. In that scenario,
instead of the generator finding every type marked with the given attribute,
the compiler would notify the generator of every type marked with the given
Expand Down Expand Up @@ -670,8 +670,8 @@ public string Serialize()
```

Obviously this is heavily simplified -- this example only handles the `string` and `int`
types properly and has no error recovery, but it should serve to demonstrate the kind
of code a source generator could add to a compilation.
types properly, adds a trailing comma to the json output and has no error recovery, but
it should serve to demonstrate the kind of code a source generator could add to a compilation.

Our next task is design a generator to generate the above code, since the
above code is itself customized in the `// Body` section according to the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,14 @@ internal override bool IsExpressionBodied
}
}

protected override bool AllowRefOrOut
{
get
{
return true;
}
}

protected override bool IsWithinExpressionOrBlockBody(int position, out int offset)
{
ConstructorDeclarationSyntax ctorSyntax = GetSyntax();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ protected sealed override void MethodChecks(DiagnosticBag diagnostics)
SyntaxToken arglistToken;
_lazyParameters = ParameterHelpers.MakeParameters(
signatureBinder, this, parameterList, out arglistToken,
allowRefOrOut: true,
allowRefOrOut: AllowRefOrOut,
allowThis: false,
addRefReadOnlyModifier: false,
diagnostics: diagnostics);
Expand All @@ -72,6 +72,8 @@ protected sealed override void MethodChecks(DiagnosticBag diagnostics)

#nullable enable
protected abstract ParameterListSyntax GetParameterList();

protected abstract bool AllowRefOrOut { get; }
#nullable restore

internal sealed override void AfterAddingTypeMembersChecks(ConversionsBase conversions, DiagnosticBag diagnostics)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@

#nullable enable

using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
Expand Down Expand Up @@ -36,13 +33,9 @@ internal RecordDeclarationSyntax GetSyntax()
return GetSyntax().PrimaryConstructorBaseType;
}

internal override bool IsExpressionBodied
{
get
{
return false;
}
}
protected override bool AllowRefOrOut => false;

internal override bool IsExpressionBodied => false;

protected override bool IsWithinExpressionOrBlockBody(int position, out int offset)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ public static IEnumerable<SyntaxKind> GetPreprocessorKeywordKinds()

public static bool IsPunctuation(SyntaxKind kind)
{
return kind >= SyntaxKind.TildeToken && kind <= SyntaxKind.PercentEqualsToken;
return kind >= SyntaxKind.TildeToken && kind <= SyntaxKind.QuestionQuestionEqualsToken;
}

public static bool IsLanguagePunctuation(SyntaxKind kind)
Expand Down
231 changes: 231 additions & 0 deletions src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15864,6 +15864,237 @@ record R(int P1, int* P2, delegate*<int> P3);";
Assert.True(p.HasPointerType);
}

[Fact, WorkItem(45008, "https://github.com/dotnet/roslyn/issues/45008")]
public void PositionalMemberModifiers_RefOrOut()
{
var src = @"
record R(ref int P1, out int P2);
";

var comp = CreateCompilation(src);
comp.VerifyEmitDiagnostics(
// (2,9): error CS0177: The out parameter 'P2' must be assigned to before control leaves the current method
// record R(ref int P1, out int P2);
Diagnostic(ErrorCode.ERR_ParamUnassigned, "(ref int P1, out int P2)").WithArguments("P2").WithLocation(2, 9),
// (2,10): error CS0631: ref and out are not valid in this context
// record R(ref int P1, out int P2);
Diagnostic(ErrorCode.ERR_IllegalRefParam, "ref").WithLocation(2, 10),
// (2,22): error CS0631: ref and out are not valid in this context
// record R(ref int P1, out int P2);
Diagnostic(ErrorCode.ERR_IllegalRefParam, "out").WithLocation(2, 22)
);
}

[Fact, WorkItem(45008, "https://github.com/dotnet/roslyn/issues/45008")]
public void PositionalMemberModifiers_RefOrOut_WithBase()
{
var src = @"
record Base(int I);
record R(ref int P1, out int P2) : Base(P2 = 1);
";

var comp = CreateCompilation(src);
comp.VerifyEmitDiagnostics(
// (3,10): error CS0631: ref and out are not valid in this context
// record R(ref int P1, out int P2) : Base(P2 = 1);
Diagnostic(ErrorCode.ERR_IllegalRefParam, "ref").WithLocation(3, 10),
// (3,22): error CS0631: ref and out are not valid in this context
// record R(ref int P1, out int P2) : Base(P2 = 1);
Diagnostic(ErrorCode.ERR_IllegalRefParam, "out").WithLocation(3, 22)
);
}

[Fact, WorkItem(45008, "https://github.com/dotnet/roslyn/issues/45008")]
public void PositionalMemberModifiers_In()
{
var src = @"
record R(in int P1);

public class C
{
public static void Main()
{
var r = new R(42);
int i = 43;
var r2 = new R(in i);
System.Console.Write((r.P1, r2.P1));
}
}
";

var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, parseOptions: TestOptions.RegularPreview, options: TestOptions.DebugExe);
comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "(42, 43)", verify: Verification.Skipped /* init-only */);

var actualMembers = comp.GetMember<NamedTypeSymbol>("R").Constructors.ToTestDisplayStrings();
var expectedMembers = new[]
{
"R..ctor(in System.Int32 P1)",
"R..ctor(R )"
};
AssertEx.Equal(expectedMembers, actualMembers);
}

[Fact, WorkItem(45008, "https://github.com/dotnet/roslyn/issues/45008")]
public void PositionalMemberModifiers_This()
{
var src = @"
record R(this int i);
";

var comp = CreateCompilation(src);
comp.VerifyEmitDiagnostics(
// (2,10): error CS0027: Keyword 'this' is not available in the current context
// record R(this int i);
Diagnostic(ErrorCode.ERR_ThisInBadContext, "this").WithLocation(2, 10)
);
}

[Fact, WorkItem(45008, "https://github.com/dotnet/roslyn/issues/45008")]
public void PositionalMemberModifiers_Params()
{
var src = @"
record R(params int[] Array);

public class C
{
public static void Main()
{
var r = new R(42, 43);
var r2 = new R(new[] { 44, 45 });
System.Console.Write((r.Array[0], r.Array[1], r2.Array[0], r2.Array[1]));
}
}
";

var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, parseOptions: TestOptions.RegularPreview, options: TestOptions.DebugExe);
comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "(42, 43, 44, 45)", verify: Verification.Skipped /* init-only */);

var actualMembers = comp.GetMember<NamedTypeSymbol>("R").Constructors.ToTestDisplayStrings();
var expectedMembers = new[]
{
"R..ctor(params System.Int32[] Array)",
"R..ctor(R )"
};
AssertEx.Equal(expectedMembers, actualMembers);
}

[Fact, WorkItem(45008, "https://github.com/dotnet/roslyn/issues/45008")]
public void PositionalMemberDefaultValue()
{
var src = @"
record R(int P = 42)
{
public static void Main()
{
var r = new R();
System.Console.Write(r.P);
}
}
";

var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, parseOptions: TestOptions.RegularPreview, options: TestOptions.DebugExe);
comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "42", verify: Verification.Skipped /* init-only */);
}

[Fact, WorkItem(45008, "https://github.com/dotnet/roslyn/issues/45008")]
public void PositionalMemberDefaultValue_AndPropertyWithInitializer()
{
var src = @"
record R(int P = 1)
{
public int P { get; init; } = 42;

public static void Main()
{
var r = new R();
System.Console.Write(r.P);
}
}
";
var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, parseOptions: TestOptions.RegularPreview, options: TestOptions.DebugExe);
comp.VerifyDiagnostics();
var verifier = CompileAndVerify(comp, expectedOutput: "42", verify: Verification.Skipped /* init-only */);

verifier.VerifyIL("R..ctor(int)", @"
{
// Code size 16 (0x10)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldc.i4.s 42
IL_0003: stfld ""int R.<P>k__BackingField""
IL_0008: ldarg.0
IL_0009: call ""object..ctor()""
IL_000e: nop
IL_000f: ret
}");
}

[Fact, WorkItem(45008, "https://github.com/dotnet/roslyn/issues/45008")]
public void PositionalMemberDefaultValue_AndPropertyWithoutInitializer()
{
var src = @"
record R(int P = 42)
{
public int P { get; init; }

public static void Main()
{
var r = new R();
System.Console.Write(r.P);
}
}
";
var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, parseOptions: TestOptions.RegularPreview, options: TestOptions.DebugExe);
comp.VerifyDiagnostics();
var verifier = CompileAndVerify(comp, expectedOutput: "0", verify: Verification.Skipped /* init-only */);

verifier.VerifyIL("R..ctor(int)", @"
{
// Code size 8 (0x8)
.maxstack 1
IL_0000: ldarg.0
IL_0001: call ""object..ctor()""
IL_0006: nop
IL_0007: ret
}");
}

[Fact, WorkItem(45008, "https://github.com/dotnet/roslyn/issues/45008")]
public void PositionalMemberDefaultValue_AndPropertyWithInitializer_CopyingParameter()
{
var src = @"
record R(int P = 42)
{
public int P { get; init; } = P;

public static void Main()
{
var r = new R();
System.Console.Write(r.P);
}
}
";
var comp = CreateCompilation(new[] { src, IsExternalInitTypeDefinition }, parseOptions: TestOptions.RegularPreview, options: TestOptions.DebugExe);
comp.VerifyDiagnostics();
var verifier = CompileAndVerify(comp, expectedOutput: "42", verify: Verification.Skipped /* init-only */);

verifier.VerifyIL("R..ctor(int)", @"
{
// Code size 15 (0xf)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: stfld ""int R.<P>k__BackingField""
IL_0007: ldarg.0
IL_0008: call ""object..ctor()""
IL_000d: nop
IL_000e: ret
}");
}

[Fact]
public void AttributesOnPrimaryConstructorParameters_01()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3141,6 +3141,7 @@ public void method()
i ^= i;
i <<= i;
i >>= i;
i ??= i;
object s = x => x + 1;
Point point;
unsafe
Expand Down Expand Up @@ -3374,6 +3375,10 @@ public void method()
Operators.GreaterThanGreaterThanEquals,
Identifier("i"),
Punctuation.Semicolon,
Identifier("i"),
Operators.QuestionQuestionEquals,
Identifier("i"),
Punctuation.Semicolon,
Keyword("object"),
Local("s"),
Operators.Equals,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ private static FormattedClassification New(string text)
public static FormattedClassification PlusEquals { get; } = New("+=");
public static FormattedClassification PlusPlus { get; } = New("++");
public static FormattedClassification QuestionMark { get; } = New("?");
public static FormattedClassification QuestionQuestionEquals { get; } = New("??=");
public static FormattedClassification Slash { get; } = New("/");
public static FormattedClassification SlashEquals { get; } = New("/=");
public static FormattedClassification Tilde { get; } = New("~");
Expand Down
Loading