Skip to content

Commit

Permalink
SemanticModel should cache synthesized bound statements in a separate…
Browse files Browse the repository at this point in the history
… cache in order to avoid “polluting” regular cache.

Fixes dotnet#10604.
Fixes dotnet#16306.
  • Loading branch information
AlekseyTs committed Apr 13, 2017
1 parent 4b8347c commit d1c662a
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -223,13 +223,8 @@ public override BoundNode Visit(BoundNode node)
/// <param name="currentBoundNode">The bound node.</param>
private bool ShouldAddNode(BoundNode currentBoundNode)
{
BoundBlock block;

// Do not add compiler generated nodes.
if (currentBoundNode.WasCompilerGenerated &&
(currentBoundNode.Kind != BoundKind.Block ||
(block = (BoundBlock)currentBoundNode).Statements.Length != 1 ||
block.Statements.Single().WasCompilerGenerated))
if (currentBoundNode.WasCompilerGenerated)
{
return false;
}
Expand Down
40 changes: 39 additions & 1 deletion src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ internal abstract partial class MemberSemanticModel : CSharpSemanticModel
private readonly ReaderWriterLockSlim _nodeMapLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
// The bound nodes associated with a syntax node, from highest in the tree to lowest.
private readonly Dictionary<SyntaxNode, ImmutableArray<BoundNode>> _guardedNodeMap = new Dictionary<SyntaxNode, ImmutableArray<BoundNode>>();
private Dictionary<SyntaxNode, BoundStatement> _lazyGuardedSynthesizedStatementsMap;

internal readonly Binder RootBinder;

Expand Down Expand Up @@ -1184,6 +1185,27 @@ private CSharpSyntaxNode GetInnermostLambdaOrQuery(CSharpSyntaxNode node, int po
return null;
}

private void GuardedAddSynthesizedStatementToMap(StatementSyntax node, BoundStatement statement)
{
if (_lazyGuardedSynthesizedStatementsMap == null)
{
_lazyGuardedSynthesizedStatementsMap = new Dictionary<SyntaxNode, BoundStatement>();
}

_lazyGuardedSynthesizedStatementsMap.Add(node, statement);
}

private BoundStatement GuardedGetSynthesizedStatementFromMap(StatementSyntax node)
{
if (_lazyGuardedSynthesizedStatementsMap != null &&
_lazyGuardedSynthesizedStatementsMap.TryGetValue(node, out BoundStatement result))
{
return result;
}

return null;
}

private ImmutableArray<BoundNode> GuardedGetBoundNodesFromMap(CSharpSyntaxNode node)
{
Debug.Assert(_nodeMapLock.IsWriteLockHeld || _nodeMapLock.IsReadLockHeld);
Expand Down Expand Up @@ -1896,12 +1918,28 @@ internal override Binder GetBinder(SyntaxNode node)
public override BoundStatement BindStatement(StatementSyntax node, DiagnosticBag diagnostics)
{
// Check the bound node cache to see if the statement was already bound.
BoundStatement synthesizedStatement = _semanticModel.GuardedGetSynthesizedStatementFromMap(node);

if (synthesizedStatement != null)
{
return synthesizedStatement;
}

ImmutableArray<BoundNode> boundNodes = _semanticModel.GuardedGetBoundNodesFromMap(node);

if (boundNodes.IsDefaultOrEmpty)
{
// Not bound already. Bind it. It will get added to the cache later by a MemberSemanticModel.NodeMapBuilder.
return base.BindStatement(node, diagnostics);
var statement = base.BindStatement(node, diagnostics);

// Synthesized statements are not added to the _guardedNodeMap, we cache them explicitly here in
// _lazyGuardedSynthesizedStatementsMap
if (statement.WasCompilerGenerated)
{
_semanticModel.GuardedAddSynthesizedStatementToMap(node, statement);
}

return statement;
}
else
{
Expand Down
30 changes: 30 additions & 0 deletions src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32882,6 +32882,36 @@ static void G(out object o)
var info = model.GetSymbolInfo(identifierAfter);
Assert.Equal("void C.G(out System.Object o)", info.Symbol.ToTestDisplayString());
}

[Fact]
[WorkItem(10604, "https://github.com/dotnet/roslyn/issues/10604")]
[WorkItem(16306, "https://github.com/dotnet/roslyn/issues/16306")]
public void GetForEachSymbolInfoWithOutVar()
{
var source =
@"using System.Collections.Generic;
public class C
{
void M()
{
foreach (var x in M2(out int i)) { }
}
IEnumerable<object> M2(out int j)
{
throw null;
}
}";
var comp = CreateStandardCompilation(source, options: TestOptions.DebugDll);
comp.VerifyDiagnostics();

var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree);
var foreachStatement = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachStatement);
Assert.Equal("System.Object", info.ElementType.ToTestDisplayString());
Assert.Equal("System.Collections.Generic.IEnumerator<System.Object> System.Collections.Generic.IEnumerable<System.Object>.GetEnumerator()",
info.GetEnumeratorMethod.ToTestDisplayString());
}
}

internal static class OutVarTestsExtensions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1528,8 +1528,7 @@ private static bool TryExtractTokenFromEmail(out string token)
}

[WorkItem(17624, "https://github.com/dotnet/roslyn/issues/17624")]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/17635"),
Trait(Traits.Feature, Traits.Features.CodeActionsInlineDeclaration)]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInlineDeclaration)]
public async Task TestInLoops3()
{
await TestInRegularAndScript1Async(
Expand Down

0 comments on commit d1c662a

Please sign in to comment.