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
18 changes: 17 additions & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -759,11 +759,11 @@ private BoundExpression CheckValue(BoundExpression expr, BindValueKind valueKind
return expr;

case BoundKind.PropertyAccess:
var propertyAccess = (BoundPropertyAccess)expr;
if (!InAttributeArgument)
{
// If the property has a synthesized backing field, record the accessor kind of the property
// access for determining whether the property access can use the backing field directly.
var propertyAccess = (BoundPropertyAccess)expr;
if (HasSynthesizedBackingField(propertyAccess.PropertySymbol, out _))
{
expr = propertyAccess.Update(
Expand All @@ -774,8 +774,24 @@ private BoundExpression CheckValue(BoundExpression expr, BindValueKind valueKind
propertyAccess.ResultKind,
propertyAccess.Type);
}
#if DEBUG
else
{
// Under DEBUG, create a new node to mark as checked, rather than mutating the original.
// This allows the original node to be passed to CheckValue multiple times safely, without
// asserting in WasPropertyBackingFieldAccessChecked.
expr = propertyAccess.Clone();
}
#endif
}
#if DEBUG
else
{
// Under DEBUG, create a new node to mark as checked, rather than mutating the original.
// This allows the original node to be passed to CheckValue multiple times safely, without
// asserting in WasPropertyBackingFieldAccessChecked.
expr = propertyAccess.Clone();
}
expr.WasPropertyBackingFieldAccessChecked = true;
#endif
break;
Expand Down
4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7755,7 +7755,7 @@ private BoundExpression BindDynamicMemberAccess(
/// </summary>
/// <remarks>
/// If new checks are added to this method, they will also need to be added to
/// <see cref="MakeQueryInvocation(CSharpSyntaxNode, BoundExpression, bool, string, SeparatedSyntaxList{TypeSyntax}, ImmutableArray{TypeWithAnnotations}, ImmutableArray{BoundExpression}, BindingDiagnosticBag, string?)"/>.
/// <see cref="MakeQueryInvocation(CSharpSyntaxNode, BoundExpression, string, SeparatedSyntaxList{TypeSyntax}, ImmutableArray{TypeWithAnnotations}, ImmutableArray{BoundExpression}, BindingDiagnosticBag, string?)"/>.
/// </remarks>
#else
/// <summary>
Expand All @@ -7764,7 +7764,7 @@ private BoundExpression BindDynamicMemberAccess(
/// </summary>
/// <remarks>
/// If new checks are added to this method, they will also need to be added to
/// <see cref="MakeQueryInvocation(CSharpSyntaxNode, BoundExpression, bool, string, SeparatedSyntaxList{TypeSyntax}, ImmutableArray{TypeWithAnnotations}, ImmutableArray{BoundExpression}, BindingDiagnosticBag)"/>.
/// <see cref="MakeQueryInvocation(CSharpSyntaxNode, BoundExpression, string, SeparatedSyntaxList{TypeSyntax}, ImmutableArray{TypeWithAnnotations}, ImmutableArray{BoundExpression}, BindingDiagnosticBag)"/>.
/// </remarks>
#endif
private BoundExpression BindMemberAccessWithBoundLeft(
Expand Down
51 changes: 19 additions & 32 deletions src/Compilers/CSharp/Portable/Binder/Binder_Query.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ internal BoundExpression BindQuery(QueryExpressionSyntax node, BindingDiagnostic
if (fromClause.Type != null)
{
var typeRestriction = BindTypeArgument(fromClause.Type, diagnostics);
cast = MakeQueryInvocation(fromClause, state.fromExpression, receiverIsCheckedForRValue: false, "Cast", fromClause.Type, typeRestriction, diagnostics
cast = MakeQueryInvocation(fromClause, state.fromExpression, "Cast", fromClause.Type, typeRestriction, diagnostics
#if DEBUG
, state.nextInvokedMethodName
#endif
Expand Down Expand Up @@ -230,7 +230,7 @@ private BoundExpression FinalTranslation(QueryTranslationState state, BindingDia
var e = state.fromExpression;
var v = selectClause.Expression;
var lambda = MakeQueryUnboundLambda(state.RangeVariableMap(), x, v, diagnostics.AccumulatesDependencies);
var result = MakeQueryInvocation(state.selectOrGroup, e, receiverIsCheckedForRValue: false, "Select", lambda, diagnostics
var result = MakeQueryInvocation(state.selectOrGroup, e, "Select", lambda, diagnostics
#if DEBUG
, state.nextInvokedMethodName
#endif
Expand Down Expand Up @@ -260,7 +260,7 @@ private BoundExpression FinalTranslation(QueryTranslationState state, BindingDia
// this is the unoptimized form (when v is not the identifier x)
var d = BindingDiagnosticBag.GetInstance(diagnostics);
BoundExpression lambdaRight = MakeQueryUnboundLambda(state.RangeVariableMap(), x, v, diagnostics.AccumulatesDependencies);
result = MakeQueryInvocation(state.selectOrGroup, e, receiverIsCheckedForRValue: false, "GroupBy", ImmutableArray.Create(lambdaLeft, lambdaRight), d
result = MakeQueryInvocation(state.selectOrGroup, e, "GroupBy", ImmutableArray.Create(lambdaLeft, lambdaRight), d
#if DEBUG
, state.nextInvokedMethodName
#endif
Expand All @@ -276,7 +276,7 @@ private BoundExpression FinalTranslation(QueryTranslationState state, BindingDia
{
// The optimized form. We store the unoptimized form for analysis
unoptimizedForm = result;
result = MakeQueryInvocation(state.selectOrGroup, e, receiverIsCheckedForRValue: false, "GroupBy", lambdaLeft, diagnostics
result = MakeQueryInvocation(state.selectOrGroup, e, "GroupBy", lambdaLeft, diagnostics
#if DEBUG
, state.nextInvokedMethodName
#endif
Expand Down Expand Up @@ -364,7 +364,7 @@ private void ReduceWhere(WhereClauseSyntax where, QueryTranslationState state, B
// is translated into
// from x in ( e ) . Where ( x => f )
var lambda = MakeQueryUnboundLambda(state.RangeVariableMap(), state.rangeVariable, where.Condition, diagnostics.AccumulatesDependencies);
var invocation = MakeQueryInvocation(where, state.fromExpression, receiverIsCheckedForRValue: false, "Where", lambda, diagnostics
var invocation = MakeQueryInvocation(where, state.fromExpression, "Where", lambda, diagnostics
#if DEBUG
, state.nextInvokedMethodName
#endif
Expand Down Expand Up @@ -395,7 +395,7 @@ private void ReduceJoin(JoinClauseSyntax join, QueryTranslationState state, Bind
// is translated into
// join x in ( e ) . Cast < T > ( ) on k1 equals k2
var castType = BindTypeArgument(join.Type, diagnostics);
castInvocation = MakeQueryInvocation(join, inExpression, receiverIsCheckedForRValue: false, "Cast", join.Type, castType, diagnostics
castInvocation = MakeQueryInvocation(join, inExpression, "Cast", join.Type, castType, diagnostics
#if DEBUG
, expectedMethodName: null
#endif
Expand Down Expand Up @@ -426,7 +426,6 @@ private void ReduceJoin(JoinClauseSyntax join, QueryTranslationState state, Bind
invocation = MakeQueryInvocation(
join,
state.fromExpression,
receiverIsCheckedForRValue: false,
"Join",
ImmutableArray.Create(inExpression, outerKeySelectorLambda, innerKeySelectorLambda, resultSelectorLambda),
diagnostics
Expand Down Expand Up @@ -455,7 +454,6 @@ private void ReduceJoin(JoinClauseSyntax join, QueryTranslationState state, Bind
invocation = MakeQueryInvocation(
join,
state.fromExpression,
receiverIsCheckedForRValue: false,
"GroupJoin",
ImmutableArray.Create(inExpression, outerKeySelectorLambda, innerKeySelectorLambda, resultSelectorLambda),
diagnostics
Expand Down Expand Up @@ -496,7 +494,6 @@ private void ReduceJoin(JoinClauseSyntax join, QueryTranslationState state, Bind
invocation = MakeQueryInvocation(
join,
state.fromExpression,
receiverIsCheckedForRValue: false,
"Join",
ImmutableArray.Create(inExpression, outerKeySelectorLambda, innerKeySelectorLambda, resultSelectorLambda),
diagnostics
Expand Down Expand Up @@ -527,7 +524,6 @@ private void ReduceJoin(JoinClauseSyntax join, QueryTranslationState state, Bind
invocation = MakeQueryInvocation(
join,
state.fromExpression,
receiverIsCheckedForRValue: false,
"GroupJoin",
ImmutableArray.Create(inExpression, outerKeySelectorLambda, innerKeySelectorLambda, resultSelectorLambda),
diagnostics
Expand Down Expand Up @@ -569,7 +565,7 @@ private void ReduceOrderBy(OrderByClauseSyntax orderby, QueryTranslationState st
{
string methodName = (first ? "OrderBy" : "ThenBy") + (ordering.IsKind(SyntaxKind.DescendingOrdering) ? "Descending" : "");
var lambda = MakeQueryUnboundLambda(state.RangeVariableMap(), state.rangeVariable, ordering.Expression, diagnostics.AccumulatesDependencies);
var invocation = MakeQueryInvocation(ordering, state.fromExpression, receiverIsCheckedForRValue: false, methodName, lambda, diagnostics
var invocation = MakeQueryInvocation(ordering, state.fromExpression, methodName, lambda, diagnostics
#if DEBUG
, state.nextInvokedMethodName
#endif
Expand Down Expand Up @@ -615,7 +611,6 @@ private void ReduceFrom(FromClauseSyntax from, QueryTranslationState state, Bind
var invocation = MakeQueryInvocation(
from,
state.fromExpression,
receiverIsCheckedForRValue: false,
"SelectMany",
ImmutableArray.Create(collectionSelectorLambda, resultSelectorLambda),
diagnostics
Expand Down Expand Up @@ -663,7 +658,6 @@ private void ReduceFrom(FromClauseSyntax from, QueryTranslationState state, Bind
var invocation = MakeQueryInvocation(
from,
state.fromExpression,
receiverIsCheckedForRValue: false,
"SelectMany",
ImmutableArray.Create(collectionSelectorLambda, resultSelectorLambda),
diagnostics
Expand Down Expand Up @@ -765,7 +759,7 @@ private void ReduceLet(LetClauseSyntax let, QueryTranslationState state, Binding
state.AddTransparentIdentifier(x.Name);
var y = state.AddRangeVariable(this, let.Identifier, diagnostics);
state.allRangeVariables[y].Add(let.Identifier.ValueText);
var invocation = MakeQueryInvocation(let, state.fromExpression, receiverIsCheckedForRValue: false, "Select", lambda, diagnostics
var invocation = MakeQueryInvocation(let, state.fromExpression, "Select", lambda, diagnostics
#if DEBUG
, state.nextInvokedMethodName
#endif
Expand Down Expand Up @@ -858,7 +852,7 @@ private UnboundLambda MakeQueryUnboundLambdaWithCast(RangeVariableMap qvm, Range
BoundExpression boundExpression = lambdaBodyBinder.BindValue(expression, diagnostics, BindValueKind.RValue);

// We transform the expression from "expr" to "expr.Cast<castTypeOpt>()".
boundExpression = lambdaBodyBinder.MakeQueryInvocation(expression, boundExpression, receiverIsCheckedForRValue: true, "Cast", castTypeSyntax, castType, diagnostics
boundExpression = lambdaBodyBinder.MakeQueryInvocation(expression, boundExpression, "Cast", castTypeSyntax, castType, diagnostics
#if DEBUG
, expectedMethodName: null
#endif
Expand All @@ -882,46 +876,46 @@ private static UnboundLambda MakeQueryUnboundLambda(CSharpSyntaxNode node, Query
return lambda;
}

protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression receiver, bool receiverIsCheckedForRValue, string methodName, BoundExpression arg, BindingDiagnosticBag diagnostics
protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression receiver, string methodName, BoundExpression arg, BindingDiagnosticBag diagnostics
#if DEBUG
, string? expectedMethodName
#endif
)
{
return MakeQueryInvocation(node, receiver, receiverIsCheckedForRValue, methodName, default(SeparatedSyntaxList<TypeSyntax>), default(ImmutableArray<TypeWithAnnotations>), ImmutableArray.Create(arg), diagnostics
return MakeQueryInvocation(node, receiver, methodName, default(SeparatedSyntaxList<TypeSyntax>), default(ImmutableArray<TypeWithAnnotations>), ImmutableArray.Create(arg), diagnostics
#if DEBUG
, expectedMethodName
#endif
);
}

protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression receiver, bool receiverIsCheckedForRValue, string methodName, ImmutableArray<BoundExpression> args, BindingDiagnosticBag diagnostics
protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression receiver, string methodName, ImmutableArray<BoundExpression> args, BindingDiagnosticBag diagnostics
#if DEBUG
, string? expectedMethodName
#endif
)
{
return MakeQueryInvocation(node, receiver, receiverIsCheckedForRValue, methodName, default(SeparatedSyntaxList<TypeSyntax>), default(ImmutableArray<TypeWithAnnotations>), args, diagnostics
return MakeQueryInvocation(node, receiver, methodName, default(SeparatedSyntaxList<TypeSyntax>), default(ImmutableArray<TypeWithAnnotations>), args, diagnostics
#if DEBUG
, expectedMethodName
#endif
);
}

protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression receiver, bool receiverIsCheckedForRValue, string methodName, TypeSyntax typeArgSyntax, TypeWithAnnotations typeArg, BindingDiagnosticBag diagnostics
protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression receiver, string methodName, TypeSyntax typeArgSyntax, TypeWithAnnotations typeArg, BindingDiagnosticBag diagnostics
#if DEBUG
, string? expectedMethodName
#endif
)
{
return MakeQueryInvocation(node, receiver, receiverIsCheckedForRValue, methodName, new SeparatedSyntaxList<TypeSyntax>(new SyntaxNodeOrTokenList(typeArgSyntax, 0)), ImmutableArray.Create(typeArg), ImmutableArray<BoundExpression>.Empty, diagnostics
return MakeQueryInvocation(node, receiver, methodName, new SeparatedSyntaxList<TypeSyntax>(new SyntaxNodeOrTokenList(typeArgSyntax, 0)), ImmutableArray.Create(typeArg), ImmutableArray<BoundExpression>.Empty, diagnostics
#if DEBUG
, expectedMethodName
#endif
);
}

protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression receiver, bool receiverIsCheckedForRValue, string methodName, SeparatedSyntaxList<TypeSyntax> typeArgsSyntax, ImmutableArray<TypeWithAnnotations> typeArgs, ImmutableArray<BoundExpression> args, BindingDiagnosticBag diagnostics
protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression receiver, string methodName, SeparatedSyntaxList<TypeSyntax> typeArgsSyntax, ImmutableArray<TypeWithAnnotations> typeArgs, ImmutableArray<BoundExpression> args, BindingDiagnosticBag diagnostics
#if DEBUG
, string? expectedMethodName
#endif
Expand Down Expand Up @@ -993,17 +987,10 @@ protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression r
}
else
{
if (!receiverIsCheckedForRValue)
var checkedUltimateReceiver = CheckValue(ultimateReceiver, BindValueKind.RValue, diagnostics);
if (checkedUltimateReceiver != ultimateReceiver)
{
var checkedUltimateReceiver = CheckValue(ultimateReceiver, BindValueKind.RValue, diagnostics);
if (checkedUltimateReceiver != ultimateReceiver)
{
receiver = updateUltimateReceiver(receiver, ultimateReceiver, checkedUltimateReceiver);
}
}
else
{
Debug.Assert(ultimateReceiver is not BoundQueryClause);
receiver = updateUltimateReceiver(receiver, ultimateReceiver, checkedUltimateReceiver);
}
}

Expand Down
11 changes: 11 additions & 0 deletions src/Compilers/CSharp/Portable/BoundTree/BoundPropertyAccess.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace Microsoft.CodeAnalysis.CSharp;

internal partial class BoundPropertyAccess
{
public BoundPropertyAccess Clone()
=> (BoundPropertyAccess)this.MemberwiseClone();
}
44 changes: 44 additions & 0 deletions src/Compilers/CSharp/Test/Semantic/Semantics/QueryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4480,6 +4480,33 @@ public void SetOnlyProperty()
var test = from i in c.Prop
select i;

public class C {
public IEnumerable<int> Prop
{
set {}
}
}
", options: TestOptions.ReleaseExe);

comp.VerifyDiagnostics(
// (6,22): error CS0154: The property or indexer 'C.Prop' cannot be used in this context because it lacks the get accessor
// var test = from i in c.Prop
Diagnostic(ErrorCode.ERR_PropertyLacksGet, "c.Prop").WithArguments("C.Prop").WithLocation(6, 22)
);
}

[Fact, WorkItem(50316, "https://github.com/dotnet/roslyn/issues/50316")]
public void SetOnlyProperty_GroupByContinuation()
{
var comp = CreateCompilation(@"
using System.Collections.Generic;
using System.Linq;

var c = new C();
var test = from i in c.Prop
group i by i into g
select g;

public class C {
public IEnumerable<int> Prop
{
Expand Down Expand Up @@ -4786,5 +4813,22 @@ public static class F
var comp = CreateCompilation(code);
CompileAndVerify(code, expectedOutput: "ran").VerifyDiagnostics();
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81709")]
public void PropertyAccess_AnonymousType_GroupBy()
{
var code = """
using System.Linq;

var x = new { A = new[] { new { B = 1 } } };

var y = from a in x.A
group a by a.B into g
select 1;
""";

var comp = CreateCompilation(code);
comp.VerifyDiagnostics();
}
}
}