diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 8ee4e477c6460..a6f519ac1e999 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -6,6 +6,7 @@ using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; @@ -1445,7 +1446,7 @@ private BoundExpression BindSizeOf(SizeOfExpressionSyntax node, BindingDiagnosti private BoundExpression BindFieldExpression(FieldExpressionSyntax node, BindingDiagnosticBag diagnostics) { Debug.Assert(ContainingType is { }); - SynthesizedBackingFieldSymbolBase? field = null; + FieldSymbol? field = null; if (hasOtherFieldSymbolInScope()) { @@ -1460,6 +1461,12 @@ private BoundExpression BindFieldExpression(FieldExpressionSyntax node, BindingD case MethodSymbol { AssociatedSymbol: SourcePropertySymbol property }: field = property.BackingField; break; + case MethodSymbol { AssociatedSymbol.OriginalDefinition: PEPropertySymbol property } method when + (Flags & BinderFlags.InEEMethodBinder) != 0 && + IsPropertyWithBackingField(property, out FieldSymbol? backingField): + + field = backingField.AsMember(method.ContainingType); + break; default: { Debug.Assert((this.Flags & BinderFlags.InContextualAttributeBinder) != 0); @@ -1495,6 +1502,22 @@ bool hasOtherFieldSymbolInScope() } } + internal static bool IsPropertyWithBackingField(PEPropertySymbol property, [NotNullWhen(true)] out FieldSymbol? backingField) + { + if (!property.GetIsNewExtensionMember() && + property.ContainingType.GetMembers(GeneratedNames.MakeBackingFieldName(property.Name)) is [FieldSymbol candidateField] && + candidateField.RefKind == property.RefKind && + candidateField.IsStatic == property.IsStatic && + candidateField.Type.Equals(property.Type, TypeCompareKind.AllIgnoreOptions)) + { + backingField = candidateField; + return true; + } + + backingField = null; + return false; + } + /// /// Report diagnostic for variable declared with name 'field' within an accessor. /// diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index a812551ee3f31..fdb193216e636 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -14054,7 +14054,7 @@ private bool IsInQuery set => _syntaxFactoryContext.IsInQuery = value; } - private bool IsInFieldKeywordContext + internal bool IsInFieldKeywordContext { get => _syntaxFactoryContext.IsInFieldKeywordContext; set => _syntaxFactoryContext.IsInFieldKeywordContext = value; diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CompilationContext.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CompilationContext.cs index cda1b5bc77c95..717a590259aff 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CompilationContext.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CompilationContext.cs @@ -120,6 +120,17 @@ internal CompilationContext( _displayClassVariables.Values.Any(v => v.Kind == DisplayClassVariableKind.This)); } + internal bool IsInFieldKeywordContext() + { + if (_currentFrame is not PEMethodSymbol) + { + return false; + } + + return (TryFindUserDefinedMethod(_currentFrame, out _) ?? _currentFrame).OriginalDefinition.AssociatedSymbol is PEPropertySymbol property && + Binder.IsPropertyWithBackingField(property, out _); + } + internal bool TryCompileExpressions( ImmutableArray syntaxNodes, string typeNameBase, @@ -209,13 +220,11 @@ private EENamedTypeSymbol CreateSynthesizedType( this, (EEMethodSymbol method, DiagnosticBag diags, out ImmutableArray declaredLocals, out ResultProperties properties) => { - var hasDisplayClassThis = GetThisProxy(_displayClassVariables) != null; var binder = ExtendBinderChain( syntax, aliases, method, NamespaceBinder, - hasDisplayClassThis, _methodNotType, out declaredLocals); @@ -248,13 +257,11 @@ internal bool TryCompileAssignment( this, (EEMethodSymbol method, DiagnosticBag diags, out ImmutableArray declaredLocals, out ResultProperties properties) => { - var hasDisplayClassThis = GetThisProxy(_displayClassVariables) != null; var binder = ExtendBinderChain( syntax, aliases, method, NamespaceBinder, - hasDisplayClassThis, methodNotType: true, out declaredLocals); @@ -954,16 +961,15 @@ private static int IndexOfMatchingAssembly(AssemblyIdentity referenceIdentity, I return -1; } - private static Binder ExtendBinderChain( + private Binder ExtendBinderChain( CSharpSyntaxNode syntax, ImmutableArray aliases, EEMethodSymbol method, Binder binder, - bool hasDisplayClassThis, bool methodNotType, out ImmutableArray declaredLocals) { - var substitutedSourceMethod = GetSubstitutedSourceMethod(method.SubstitutedSourceMethod, hasDisplayClassThis); + var substitutedSourceMethod = GetSubstitutedUserDefinedSourceMethod(method.SubstitutedSourceMethod); var substitutedSourceType = substitutedSourceMethod.ContainingType; var stack = ArrayBuilder.GetInstance(); @@ -1804,9 +1810,6 @@ private static NamedTypeSymbol GetNonDisplayClassContainer(NamedTypeSymbol type) /// The symbol of the method that is currently on top of the callstack, with /// EE type parameters substituted in place of the original type parameters. /// - /// - /// True if "this" is available via a display class in the current context. - /// /// /// If is compiler-generated, /// then we will attempt to determine which user-defined method caused it to be @@ -1826,12 +1829,30 @@ private static NamedTypeSymbol GetNonDisplayClassContainer(NamedTypeSymbol type) /// properties that are used during binding (e.g. static-ness, generic arity, type parameter /// constraints). /// - internal static MethodSymbol GetSubstitutedSourceMethod( - MethodSymbol candidateSubstitutedSourceMethod, - bool sourceMethodMustBeInstance) + internal MethodSymbol GetSubstitutedUserDefinedSourceMethod( + MethodSymbol candidateSubstitutedSourceMethod) { Debug.Assert(candidateSubstitutedSourceMethod.DeclaringCompilation is not null); + MethodSymbol? userDefinedMethod = TryFindUserDefinedMethod(candidateSubstitutedSourceMethod, out ImmutableArray displayClassTypeArguments); + + if (userDefinedMethod is not null) + { + MethodSymbol sourceMethod = new EECompilationContextMethod(candidateSubstitutedSourceMethod.DeclaringCompilation!, userDefinedMethod.OriginalDefinition); + sourceMethod = sourceMethod.AsMember(userDefinedMethod.ContainingType); + + return displayClassTypeArguments.Length == 0 + ? sourceMethod + : sourceMethod.Construct(displayClassTypeArguments); + } + + return candidateSubstitutedSourceMethod; + } + + private MethodSymbol? TryFindUserDefinedMethod(MethodSymbol candidateSubstitutedSourceMethod, out ImmutableArray displayClassTypeArguments) + { + displayClassTypeArguments = default; + var candidateSubstitutedSourceType = candidateSubstitutedSourceMethod.ContainingType; string? desiredMethodName; @@ -1839,6 +1860,8 @@ internal static MethodSymbol GetSubstitutedSourceMethod( GeneratedNameParser.TryParseSourceMethodNameFromGeneratedName(candidateSubstitutedSourceMethod.Name, GeneratedNameKind.LambdaMethod, out desiredMethodName) || GeneratedNameParser.TryParseSourceMethodNameFromGeneratedName(candidateSubstitutedSourceMethod.Name, GeneratedNameKind.LocalFunction, out desiredMethodName)) { + bool sourceMethodMustBeInstance = GetThisProxy(_displayClassVariables) != null; + // We could be in the MoveNext method of an async lambda. string? tempMethodName; if (GeneratedNameParser.TryParseSourceMethodNameFromGeneratedName(desiredMethodName, GeneratedNameKind.LambdaMethod, out tempMethodName) || @@ -1865,19 +1888,15 @@ internal static MethodSymbol GetSubstitutedSourceMethod( { if (IsViableSourceMethod(candidateMethod, desiredMethodName, desiredTypeParameters, sourceMethodMustBeInstance)) { - MethodSymbol sourceMethod = new EECompilationContextMethod(candidateSubstitutedSourceMethod.DeclaringCompilation!, candidateMethod.OriginalDefinition); - sourceMethod = sourceMethod.AsMember(substitutedSourceType); - - return desiredTypeParameters.Length == 0 - ? sourceMethod - : sourceMethod.Construct(candidateSubstitutedSourceType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics); + displayClassTypeArguments = candidateSubstitutedSourceType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics; + return candidateMethod; } } Debug.Fail("Why didn't we find a substituted source method for " + candidateSubstitutedSourceMethod + "?"); } - return candidateSubstitutedSourceMethod; + return null; } private static bool IsViableSourceMethod( diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs index d074738506645..99c4348ba7ce6 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs @@ -222,14 +222,15 @@ internal CompilationContext CreateCompilationContext() out ImmutableArray methodTokens, out ImmutableArray errorMessages) { + var context = CreateCompilationContext(); + bool isInFieldKeywordContext = context.IsInFieldKeywordContext(); var diagnostics = DiagnosticBag.GetInstance(); - var syntaxNodes = expressions.SelectAsArray(expr => Parse(expr, treatAsExpression: true, diagnostics, out var formatSpecifiers)); + var syntaxNodes = expressions.SelectAsArray(expr => Parse(expr, isInFieldKeywordContext, treatAsExpression: true, diagnostics, out var formatSpecifiers)); byte[]? assembly = null; if (!diagnostics.HasAnyErrors()) { RoslynDebug.Assert(syntaxNodes.All(s => s != null)); - var context = CreateCompilationContext(); if (context.TryCompileExpressions(syntaxNodes!, TypeName, MethodName, diagnostics, out var moduleBuilder)) { using var stream = new MemoryStream(); @@ -280,14 +281,15 @@ internal CompilationContext CreateCompilationContext() out ResultProperties resultProperties, CompilationTestData? testData) { - var syntax = Parse(expr, (compilationFlags & DkmEvaluationFlags.TreatAsExpression) != 0, diagnostics, out var formatSpecifiers); + var context = CreateCompilationContext(); + + var syntax = Parse(expr, context.IsInFieldKeywordContext(), (compilationFlags & DkmEvaluationFlags.TreatAsExpression) != 0, diagnostics, out var formatSpecifiers); if (syntax == null) { resultProperties = default; return null; } - var context = CreateCompilationContext(); if (!context.TryCompileExpression(syntax, TypeName, MethodName, aliases, testData, diagnostics, out var moduleBuilder, out var synthesizedMethod)) { resultProperties = default; @@ -325,8 +327,9 @@ internal CompilationContext CreateCompilationContext() formatSpecifiers: formatSpecifiers); } - private static CSharpSyntaxNode? Parse( + private CSharpSyntaxNode? Parse( string expr, + bool isInFieldKeywordContext, bool treatAsExpression, DiagnosticBag diagnostics, out ReadOnlyCollection? formatSpecifiers) @@ -335,7 +338,7 @@ internal CompilationContext CreateCompilationContext() { // Try to parse as a statement. If that fails, parse as an expression. var statementDiagnostics = DiagnosticBag.GetInstance(); - var statementSyntax = expr.ParseStatement(statementDiagnostics); + var statementSyntax = expr.ParseStatement(isInFieldKeywordContext, statementDiagnostics); Debug.Assert((statementSyntax == null) || !statementDiagnostics.HasAnyErrors()); statementDiagnostics.Free(); var isExpressionStatement = statementSyntax.IsKind(SyntaxKind.ExpressionStatement); @@ -353,7 +356,7 @@ internal CompilationContext CreateCompilationContext() } } - return expr.ParseExpression(diagnostics, allowFormatSpecifiers: true, out formatSpecifiers); + return expr.ParseExpression(isInFieldKeywordContext, diagnostics, allowFormatSpecifiers: true, out formatSpecifiers); } internal override CompileResult? CompileAssignment( @@ -364,14 +367,14 @@ internal CompilationContext CreateCompilationContext() out ResultProperties resultProperties, CompilationTestData? testData) { - var assignment = target.ParseAssignment(expr, diagnostics); + var context = CreateCompilationContext(); + var assignment = target.ParseAssignment(expr, context.IsInFieldKeywordContext(), diagnostics); if (assignment == null) { resultProperties = default; return null; } - var context = CreateCompilationContext(); if (!context.TryCompileAssignment(assignment, TypeName, MethodName, aliases, testData, diagnostics, out var moduleBuilder, out var synthesizedMethod)) { resultProperties = default; diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SyntaxHelpers.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SyntaxHelpers.cs index 9db98697d1e0d..32b09356dba0d 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SyntaxHelpers.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/SyntaxHelpers.cs @@ -22,6 +22,7 @@ internal static class SyntaxHelpers /// internal static ExpressionSyntax? ParseExpression( this string expr, + bool isInFieldKeywordContext, DiagnosticBag diagnostics, bool allowFormatSpecifiers, out ReadOnlyCollection? formatSpecifiers) @@ -35,7 +36,7 @@ internal static class SyntaxHelpers allowFormatSpecifiers = false; } - var syntax = ParseDebuggerExpression(expr, consumeFullText: !allowFormatSpecifiers); + var syntax = ParseDebuggerExpression(expr, consumeFullText: !allowFormatSpecifiers, isInFieldKeywordContext); diagnostics.AddRange(syntax.GetDiagnostics()); formatSpecifiers = null; @@ -56,10 +57,11 @@ internal static class SyntaxHelpers internal static ExpressionSyntax? ParseAssignment( this string target, string expr, + bool isInFieldKeywordContext, DiagnosticBag diagnostics) { var text = SourceText.From(expr, encoding: null, SourceHashAlgorithms.Default); - var expression = ParseDebuggerExpressionInternal(text, consumeFullText: true); + var expression = ParseDebuggerExpressionInternal(text, consumeFullText: true, isInFieldKeywordContext); // We're creating a SyntaxTree for just the RHS so that the Diagnostic spans for parse errors // will be correct (with respect to the original input text). If we ever expose a SemanticModel // for debugger expressions, we should use this SyntaxTree. @@ -72,7 +74,7 @@ internal static class SyntaxHelpers // Any Diagnostic spans produced in binding will be offset by the length of the "target" expression text. // If we want to support live squiggles in debugger windows, SemanticModel, etc, we'll want to address this. - var targetSyntax = ParseDebuggerExpressionInternal(SourceText.From(target, encoding: null, SourceHashAlgorithms.Default), consumeFullText: true); + var targetSyntax = ParseDebuggerExpressionInternal(SourceText.From(target, encoding: null, SourceHashAlgorithms.Default), consumeFullText: true, isInFieldKeywordContext); Debug.Assert(!targetSyntax.GetDiagnostics().Any(), "The target of an assignment should never contain Diagnostics if we're being allowed to assign to it in the debugger."); var assignment = InternalSyntax.SyntaxFactory.AssignmentExpression( @@ -88,9 +90,10 @@ internal static class SyntaxHelpers /// internal static StatementSyntax? ParseStatement( this string expr, + bool isInFieldKeywordContext, DiagnosticBag diagnostics) { - var syntax = ParseDebuggerStatement(expr); + var syntax = ParseDebuggerStatement(expr, isInFieldKeywordContext); diagnostics.AddRange(syntax.GetDiagnostics()); return diagnostics.HasAnyErrors() ? null : syntax; } @@ -194,17 +197,18 @@ private static bool RemoveSemicolonIfAny(ref string str) return false; } - private static ExpressionSyntax ParseDebuggerExpression(string text, bool consumeFullText) + private static ExpressionSyntax ParseDebuggerExpression(string text, bool consumeFullText, bool isInFieldKeywordContext) { var source = SourceText.From(text, encoding: null, SourceHashAlgorithms.Default); - var expression = ParseDebuggerExpressionInternal(source, consumeFullText); + var expression = ParseDebuggerExpressionInternal(source, consumeFullText, isInFieldKeywordContext); return expression.MakeDebuggerExpression(source); } - private static InternalSyntax.ExpressionSyntax ParseDebuggerExpressionInternal(SourceText source, bool consumeFullText) + private static InternalSyntax.ExpressionSyntax ParseDebuggerExpressionInternal(SourceText source, bool consumeFullText, bool isInFieldKeywordContext) { using var lexer = new InternalSyntax.Lexer(source, PreviewParseOptions, allowPreprocessorDirectives: false); using var parser = new InternalSyntax.LanguageParser(lexer, oldTree: null, changes: null, lexerMode: InternalSyntax.LexerMode.DebuggerSyntax); + parser.IsInFieldKeywordContext = isInFieldKeywordContext; var node = parser.ParseExpression(); if (consumeFullText) @@ -212,11 +216,12 @@ private static InternalSyntax.ExpressionSyntax ParseDebuggerExpressionInternal(S return node; } - private static StatementSyntax ParseDebuggerStatement(string text) + private static StatementSyntax ParseDebuggerStatement(string text, bool isInFieldKeywordContext) { var source = SourceText.From(text, encoding: null, SourceHashAlgorithms.Default); using var lexer = new InternalSyntax.Lexer(source, PreviewParseOptions); using var parser = new InternalSyntax.LanguageParser(lexer, oldTree: null, changes: null, lexerMode: InternalSyntax.LexerMode.DebuggerSyntax); + parser.IsInFieldKeywordContext = isInFieldKeywordContext; var statement = parser.ParseStatement(); var syntaxTree = statement.CreateSyntaxTree(source); diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/CompileExpressionsTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/CompileExpressionsTests.cs index b0e9c7f271fcd..cab15530e613d 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/CompileExpressionsTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/CompileExpressionsTests.cs @@ -1535,5 +1535,81 @@ .locals init (int V_0, //x locals.Free(); }); } + + [Fact] + public void FieldKeyword_01() + { + var source = +@" +class C +{ + public int P1 + { + get + { + return field; + } + set + { + field = value; + } + } + + public int P2 + { + get + { +#line 100 + return field; +#line 200 + } + set + { + field = value; + } + } + + public int P3 + { + get + { + return field; + } + set + { + field = value; + } + } +} +"; + var comp = CreateCompilation(source, options: TestOptions.DebugDll); + WithRuntimeInstance( + comp, + references: null, + includeLocalSignatures: true, + includeIntrinsicAssembly: false, + validator: runtime => + { + var context = CreateMethodContext(runtime, "C.get_P2", atLineNumber: 100); + var assembly = context.CompileExpressions( + ImmutableArray.Create("field"), + out var methodTokens, + out var errorMessages); + Assert.NotNull(assembly); + Assert.True(errorMessages.IsEmpty); + Assert.Equal(1, methodTokens.Length); + assembly.VerifyIL(methodTokens[0], "<>x0.<>m0", +@" +Locals: int32 +{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: ldfld 0x0A000006 + IL_0006: ret +} +"); + }); + } } } diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/DeclarationTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/DeclarationTests.cs index a1c176bf3602f..fe013c08f5e36 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/DeclarationTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/DeclarationTests.cs @@ -2204,5 +2204,89 @@ static void M() Assert.Equal("error CS0136: A local or parameter named 'x' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter", error); }); } + + [ConditionalFact(typeof(IsRelease), Reason = "https://github.com/dotnet/roslyn/issues/25702")] + public void FieldKeyword_01() + { + var source = +@" +class C +{ + public int P1 + { + get + { + return field; + } + set + { + field = value; + } + } + + public int P2 + { + get + { +#line 100 + return field; +#line 200 + } + set + { + field = value; + } + } + + public int P3 + { + get + { + return field; + } + set + { + field = value; + } + } +} +"; + var compilation0 = CreateCompilation(source, options: TestOptions.DebugDll); + WithRuntimeInstance(compilation0, runtime => + { + var context = CreateMethodContext(runtime, "C.get_P2", atLineNumber: 100); + string error; + var testData = new CompilationTestData(); + context.CompileExpression( + "int b = field;", + DkmEvaluationFlags.None, + NoAliases, + out error, + testData); + testData.GetMethodData("<>x.<>m0").VerifyIL( +@" +{ + // Code size 48 (0x30) + .maxstack 4 + .locals init (int V_0, + System.Guid V_1) + IL_0000: ldtoken ""int"" + IL_0005: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" + IL_000a: ldstr ""b"" + IL_000f: ldloca.s V_1 + IL_0011: initobj ""System.Guid"" + IL_0017: ldloc.1 + IL_0018: ldnull + IL_0019: call ""void Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.CreateVariable(System.Type, string, System.Guid, byte[])"" + IL_001e: ldstr ""b"" + IL_0023: call ""int Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress(string)"" + IL_0028: ldarg.0 + IL_0029: ldfld ""int C.k__BackingField"" + IL_002e: stind.i4 + IL_002f: ret +} +"); + }); + } } } diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs index 66e13233e09f7..9fdba7ca87e7d 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs @@ -11253,5 +11253,624 @@ .maxstack 2 IL_0008: ret }"); } + + [Fact] + public void FieldKeyword_01() + { + var source = @" +class C +{ + public int P1 + { + get + { + return field; + } + set + { + field = value; + } + } + + public int P2 + { + get + { +#line 100 + return 15; +#line 200 + } + set + { + field = value; + } + } + + public int P3 + { + get + { + return field; + } + set + { + field = value; + } + } +} +"; + + var testData = Evaluate( + source, + OutputKind.DynamicallyLinkedLibrary, + methodName: "C.get_P2", + atLineNumber: 100, debugFormat: DebugInformationFormat.PortablePdb, + expr: "field"); + + var methodData = testData.GetMethodData("<>x.<>m0"); + + Assert.True(methodData.Method.IsStatic); + AssertEx.Equal("System.Int32 <>x.<>m0(C <>4__this)", ((MethodSymbol)methodData.Method).ToTestDisplayString()); + methodData.VerifyIL( +@" +{ + // Code size 7 (0x7) + .maxstack 1 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldfld ""int C.k__BackingField"" + IL_0006: ret +} +"); + } + + [Fact] + public void FieldKeyword_02() + { + var source = @" +class C +{ + public int P + { + get + { + return field; + } + set + { +#line 300 + _ = value; +#line 400 + } + } +} +"; + + var testData = Evaluate( + source, + OutputKind.DynamicallyLinkedLibrary, + methodName: "C.set_P", + atLineNumber: 300, debugFormat: DebugInformationFormat.PortablePdb, + expr: "field"); + + var methodData = testData.GetMethodData("<>x.<>m0"); + + Assert.True(methodData.Method.IsStatic); + AssertEx.Equal("System.Int32 <>x.<>m0(C <>4__this, System.Int32 value)", ((MethodSymbol)methodData.Method).ToTestDisplayString()); + methodData.VerifyIL( +@" +{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: ldfld ""int C.

k__BackingField"" + IL_0006: ret +} +"); + } + + [Fact] + public void FieldKeyword_03() + { + var source = @" +class C +{ + public int P + { + get + { + return local(); + + int local() + { +#line 100 + return field; +#line 200 + } + } + set + { + field = value; + } + } +} +"; + + var testData = Evaluate( + source, + OutputKind.DynamicallyLinkedLibrary, + methodName: "C.g__local|2_0", + atLineNumber: 100, debugFormat: DebugInformationFormat.PortablePdb, + expr: "field"); + + var methodData = testData.GetMethodData("<>x.<>m0"); + + Assert.True(methodData.Method.IsStatic); + AssertEx.Equal("System.Int32 <>x.<>m0(C <>4__this)", ((MethodSymbol)methodData.Method).ToTestDisplayString()); + methodData.VerifyIL( +@" +{ + // Code size 7 (0x7) + .maxstack 1 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldfld ""int C.

k__BackingField"" + IL_0006: ret +} +"); + } + + [Fact] + public void FieldKeyword_04() + { + var source = @" +class C +{ + public int P + { + get + { + int x = 1; + System.Func d = local; + return d(); + + int local() + { +#line 100 + return field + x; +#line 200 + } + } + set + { + field = value; + } + } +} +"; + + var testData = Evaluate( + source, + OutputKind.DynamicallyLinkedLibrary, + methodName: "C.<>c__DisplayClass2_0.g__local|0", + atLineNumber: 100, debugFormat: DebugInformationFormat.PortablePdb, + expr: "field"); + + var methodData = testData.GetMethodData("<>x.<>m0"); + + Assert.True(methodData.Method.IsStatic); + AssertEx.Equal("System.Int32 <>x.<>m0(C.<>c__DisplayClass2_0 <>4__this)", ((MethodSymbol)methodData.Method).ToTestDisplayString()); + methodData.VerifyIL( +@" +{ + // Code size 12 (0xc) + .maxstack 1 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldfld ""C C.<>c__DisplayClass2_0.<>4__this"" + IL_0006: ldfld ""int C.

k__BackingField"" + IL_000b: ret +} +"); + } + + [Fact] + public void FieldKeyword_05() + { + var source = @" +class C +{ + public int P + { + get + { + System.Func d = int () => + { +#line 100 + return field; +#line 200 + }; + + return d(); + } + set + { + field = value; + } + } +} +"; + + var testData = Evaluate( + source, + OutputKind.DynamicallyLinkedLibrary, + methodName: "C.b__2_0", + atLineNumber: 100, debugFormat: DebugInformationFormat.PortablePdb, + expr: "field"); + + var methodData = testData.GetMethodData("<>x.<>m0"); + + Assert.True(methodData.Method.IsStatic); + AssertEx.Equal("System.Int32 <>x.<>m0(C <>4__this)", ((MethodSymbol)methodData.Method).ToTestDisplayString()); + methodData.VerifyIL( +@" +{ + // Code size 7 (0x7) + .maxstack 1 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldfld ""int C.

k__BackingField"" + IL_0006: ret +} +"); + } + + [Fact] + public void FieldKeyword_06() + { + var source = @" +class C +{ + public int P + { + get + { + int x = 1; + + System.Func d = int () => + { +#line 100 + return field + x; +#line 200 + }; + + return d(); + } + set + { + field = value; + } + } +} +"; + + var testData = Evaluate( + source, + OutputKind.DynamicallyLinkedLibrary, + methodName: "C.<>c__DisplayClass2_0.b__0", + atLineNumber: 100, debugFormat: DebugInformationFormat.PortablePdb, + expr: "field"); + + var methodData = testData.GetMethodData("<>x.<>m0"); + + Assert.True(methodData.Method.IsStatic); + AssertEx.Equal("System.Int32 <>x.<>m0(C.<>c__DisplayClass2_0 <>4__this)", ((MethodSymbol)methodData.Method).ToTestDisplayString()); + methodData.VerifyIL( +@" +{ + // Code size 12 (0xc) + .maxstack 1 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldfld ""C C.<>c__DisplayClass2_0.<>4__this"" + IL_0006: ldfld ""int C.

k__BackingField"" + IL_000b: ret +} +"); + } + + [Fact] + public void FieldKeyword_07() + { + var source = @" +class C +{ + public System.Collections.Generic.IEnumerable P + { + get + { + foreach (var i in field) + { +#line 100 + yield return i; +#line 200 + } + } + } = []; +} +"; + + var testData = Evaluate( + source, + OutputKind.DynamicallyLinkedLibrary, + methodName: "C.d__2.MoveNext", + atLineNumber: 100, debugFormat: DebugInformationFormat.PortablePdb, + expr: "field"); + + var methodData = testData.GetMethodData("<>x.<>m0"); + + Assert.True(methodData.Method.IsStatic); + AssertEx.Equal("System.Collections.Generic.IEnumerable <>x.<>m0(C.d__2 <>4__this)", ((MethodSymbol)methodData.Method).ToTestDisplayString()); + methodData.VerifyIL( +@" +{ + // Code size 12 (0xc) + .maxstack 1 + .locals init (bool V_0, + int V_1) + IL_0000: ldarg.0 + IL_0001: ldfld ""C C.d__2.<>4__this"" + IL_0006: ldfld ""System.Collections.Generic.IEnumerable C.

k__BackingField"" + IL_000b: ret +} +"); + } + + [Fact] + public void FieldKeyword_08() + { + var source = @" +class C +{ + public T P2 + { + get + { +#line 100 + return field; +#line 200 + } + set + { + field = value; + } + } +} +"; + + var testData = Evaluate( + source, + OutputKind.DynamicallyLinkedLibrary, + methodName: "C.get_P2", + atLineNumber: 100, debugFormat: DebugInformationFormat.PortablePdb, + expr: "field"); + + var methodData = testData.GetMethodData("<>x.<>m0"); + + Assert.True(methodData.Method.IsStatic); + AssertEx.Equal("T <>x.<>m0(C <>4__this)", ((MethodSymbol)methodData.Method).ToTestDisplayString()); + methodData.VerifyIL( +@" +{ + // Code size 7 (0x7) + .maxstack 1 + .locals init (T V_0) + IL_0000: ldarg.0 + IL_0001: ldfld ""T C.k__BackingField"" + IL_0006: ret +} +"); + } + + [Fact] + public void FieldKeyword_09() + { + var source = +@" +class C +{ + public int P1 + { + get + { + return field; + } + set + { + field = value; + } + } + + public int P2 + { + get + { +#line 100 + return field; +#line 200 + } + set + { + field = value; + } + } + + public int P3 + { + get + { + return field; + } + set + { + field = value; + } + } +} +"; + var compilation0 = CreateCompilation(source, options: TestOptions.UnsafeDebugDll); + WithRuntimeInstance(compilation0, runtime => + { + var context = CreateMethodContext(runtime, "C.get_P2", atLineNumber: 100); + string error; + var testData = new CompilationTestData(); + context.CompileAssignment( + target: "field", + expr: "field + 1", + error: out error, + testData: testData); + testData.GetMethodData("<>x.<>m0").VerifyIL( +@" +{ + // Code size 15 (0xf) + .maxstack 3 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldarg.0 + IL_0002: ldfld ""int C.k__BackingField"" + IL_0007: ldc.i4.1 + IL_0008: add + IL_0009: stfld ""int C.k__BackingField"" + IL_000e: ret +} +"); + }); + } + + [Fact] + public void FieldKeyword_10() + { + var source = @" +class C +{ + public static int P1 + { + get + { + return field; + } + set + { + field = value; + } + } + + public static int P2 + { + get + { +#line 100 + return field; +#line 200 + } + set + { + field = value; + } + } + + public static int P3 + { + get + { + return field; + } + set + { + field = value; + } + } +} +"; + + var testData = Evaluate( + source, + OutputKind.DynamicallyLinkedLibrary, + methodName: "C.get_P2", + atLineNumber: 100, debugFormat: DebugInformationFormat.PortablePdb, + expr: "field"); + + var methodData = testData.GetMethodData("<>x.<>m0"); + + Assert.True(methodData.Method.IsStatic); + AssertEx.Equal("System.Int32 <>x.<>m0()", ((MethodSymbol)methodData.Method).ToTestDisplayString()); + methodData.VerifyIL( +@" +{ + // Code size 6 (0x6) + .maxstack 1 + .locals init (int V_0) + IL_0000: ldsfld ""int C.k__BackingField"" + IL_0005: ret +} +"); + } + + [Fact] + public void FieldKeyword_11() + { + var source = @" +class C +{ + public static int P1 + { + get + { + return field; + } + set + { + field = value; + } + } + + public static int P2 + { + get + { +#line 100 + throw null; +#line 200 + } + set + { + } + } + + public static int P3 + { + get + { + return field; + } + set + { + field = value; + } + } +} +"; + + var testData = Evaluate( + source, + OutputKind.DynamicallyLinkedLibrary, + methodName: "C.get_P2", + atLineNumber: 100, debugFormat: DebugInformationFormat.PortablePdb, + expr: "field", + resultProperties: out _, + error: out string error); + + Assert.Equal("error CS0103: The name 'field' does not exist in the current context", error); + } } } diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/HoistedThisTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/HoistedThisTests.cs index 5c8a2b590dd1e..a27352f88ef39 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/HoistedThisTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/HoistedThisTests.cs @@ -1435,7 +1435,8 @@ private static void CheckIteratorOverloading(string source, Func().Single(t => GeneratedNameParser.GetKind(t.Name) == GeneratedNameKind.StateMachineType); var moveNextMethod = stateMachineType.GetMember("MoveNext"); - var guessedIterator = CompilationContext.GetSubstitutedSourceMethod(new EECompilationContextMethod(comp2, moveNextMethod), sourceMethodMustBeInstance: true); + var guessedIterator = new CompilationContext(comp2, currentFrame: moveNextMethod, currentSourceMethod: moveNextMethod, locals: [], inScopeHoistedLocalSlots: [], methodDebugInfo: MethodDebugInfo.None). + GetSubstitutedUserDefinedSourceMethod(new EECompilationContextMethod(comp2, moveNextMethod)); Assert.Equal(iteratorMethod, ((EECompilationContextMethod)guessedIterator.OriginalDefinition).UnderlyingMethod.OriginalDefinition); } }