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: 20 additions & 12 deletions src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,24 @@ private bool ContainsPlaceholderScope(BoundValuePlaceholderBase placeholder)
public override BoundNode? Visit(BoundNode? node)
{
#if DEBUG
TrackVisit(node);
#endif
return base.Visit(node);
}

#if DEBUG
protected override void BeforeVisitingSkippedBoundBinaryOperatorChildren(BoundBinaryOperator node)
{
TrackVisit(node);
}

protected override void BeforeVisitingSkippedBoundCallChildren(BoundCall node)
{
TrackVisit(node);
}

private void TrackVisit(BoundNode? node)
{
if (node is BoundValuePlaceholderBase placeholder)
{
Debug.Assert(ContainsPlaceholderScope(placeholder));
Expand All @@ -267,14 +285,11 @@ private bool ContainsPlaceholderScope(BoundValuePlaceholderBase placeholder)
if (_visited is { } && _visited.Count <= MaxTrackVisited)
{
bool added = _visited.Add(expr);
Debug.Assert(added);
Debug.Assert(added, $"Expression {expr} `{expr.Syntax}` visited more than once.");
}
}
#endif
return base.Visit(node);
}

#if DEBUG
private void AssertVisited(BoundExpression expr)
{
if (expr is BoundValuePlaceholderBase placeholder)
Expand All @@ -283,7 +298,7 @@ private void AssertVisited(BoundExpression expr)
}
else if (_visited is { } && _visited.Count <= MaxTrackVisited)
{
Debug.Assert(_visited.Contains(expr));
Debug.Assert(_visited.Contains(expr), $"Expected {expr} `{expr.Syntax}` to be visited.");
}
}
#endif
Expand Down Expand Up @@ -659,13 +674,6 @@ protected override void VisitArguments(BoundCall node)
_localScopeDepth,
_diagnostics);
}

#if DEBUG
if (_visited is { } && _visited.Count <= MaxTrackVisited)
{
_visited.Add(node);
}
#endif
}

private void GetInterpolatedStringPlaceholders(
Expand Down
13 changes: 13 additions & 0 deletions src/Compilers/CSharp/Portable/BoundTree/BoundTreeWalker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,15 @@ protected BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator

var binary = (BoundBinaryOperator)node.Left;

BeforeVisitingSkippedBoundBinaryOperatorChildren(binary);
rightOperands.Push(binary.Right);

BoundExpression current = binary.Left;

while (current.Kind == BoundKind.BinaryOperator)
{
binary = (BoundBinaryOperator)current;
BeforeVisitingSkippedBoundBinaryOperatorChildren(binary);
rightOperands.Push(binary.Right);
current = binary.Left;
}
Expand All @@ -136,6 +138,10 @@ protected BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator
return null;
}

protected virtual void BeforeVisitingSkippedBoundBinaryOperatorChildren(BoundBinaryOperator node)
{
}

public sealed override BoundNode? VisitCall(BoundCall node)
{
if (node.ReceiverOpt is BoundCall receiver1)
Expand All @@ -147,10 +153,13 @@ protected BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator
node = receiver1;
while (node.ReceiverOpt is BoundCall receiver2)
{
BeforeVisitingSkippedBoundCallChildren(node);
calls.Push(node);
node = receiver2;
}

BeforeVisitingSkippedBoundCallChildren(node);

VisitReceiver(node);

do
Expand All @@ -170,6 +179,10 @@ protected BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator
return null;
}

protected virtual void BeforeVisitingSkippedBoundCallChildren(BoundCall node)
{
}

/// <summary>
/// Called only for the first (in evaluation order) <see cref="BoundCall"/> in the chain.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7871,6 +7871,37 @@ ref struct S
Diagnostic(ErrorCode.ERR_EscapeVariable, "s").WithArguments("s").WithLocation(47, 16));
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")]
public void UserDefinedBinaryOperator_RefStruct_Nested()
{
var source = """
class C
{
S M()
{
S s;
s = default(S) + 100 + 200;
return s;
}
}

ref struct S
{
public static S operator+(S y, in int x) => throw null;
}
""";
CreateCompilation(source).VerifyDiagnostics(
// (6,13): error CS8347: Cannot use a result of 'S.operator +(S, in int)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope
// s = default(S) + 100 + 200;
Diagnostic(ErrorCode.ERR_EscapeCall, "default(S) + 100").WithArguments("S.operator +(S, in int)", "x").WithLocation(6, 13),
// (6,13): error CS8347: Cannot use a result of 'S.operator +(S, in int)' in this context because it may expose variables referenced by parameter 'y' outside of their declaration scope
// s = default(S) + 100 + 200;
Diagnostic(ErrorCode.ERR_EscapeCall, "default(S) + 100 + 200").WithArguments("S.operator +(S, in int)", "y").WithLocation(6, 13),
// (6,26): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference
// s = default(S) + 100 + 200;
Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "100").WithLocation(6, 26));
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")]
public void UserDefinedBinaryOperator_RefStruct_Scoped_Left()
{
Expand Down Expand Up @@ -8531,5 +8562,16 @@ static R F2()
// return new R(1) | new R(2);
Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "1").WithLocation(18, 22));
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72873")]
public void Utf8Addition()
{
var code = """
using System;
ReadOnlySpan<byte> x = "Hello"u8 + " "u8 + "World!"u8;
Console.WriteLine(x.Length);
""";
CreateCompilation(code, targetFramework: TargetFramework.Net70).VerifyDiagnostics();
}
}
}