From d1c662a45f60b05275fab0cb79ae184193074ef9 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Wed, 12 Apr 2017 21:50:54 -0700 Subject: [PATCH] =?UTF-8?q?SemanticModel=20should=20cache=20synthesized=20?= =?UTF-8?q?bound=20statements=20in=20a=20separate=20cache=20in=20order=20t?= =?UTF-8?q?o=20avoid=20=E2=80=9Cpolluting=E2=80=9D=20regular=20cache.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #10604. Fixes #16306. --- .../MemberSemanticModel.NodeMapBuilder.cs | 7 +--- .../Compilation/MemberSemanticModel.cs | 40 ++++++++++++++++++- .../Test/Semantic/Semantics/OutVarTests.cs | 30 ++++++++++++++ .../CSharpInlineDeclarationTests.cs | 3 +- 4 files changed, 71 insertions(+), 9 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.NodeMapBuilder.cs b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.NodeMapBuilder.cs index 4d71ba03c7188..f6d1866180c4d 100644 --- a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.NodeMapBuilder.cs +++ b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.NodeMapBuilder.cs @@ -223,13 +223,8 @@ public override BoundNode Visit(BoundNode node) /// The bound node. 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; } diff --git a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs index e00dc3d234dca..e4e42d2129977 100644 --- a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs @@ -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> _guardedNodeMap = new Dictionary>(); + private Dictionary _lazyGuardedSynthesizedStatementsMap; internal readonly Binder RootBinder; @@ -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(); + } + + _lazyGuardedSynthesizedStatementsMap.Add(node, statement); + } + + private BoundStatement GuardedGetSynthesizedStatementFromMap(StatementSyntax node) + { + if (_lazyGuardedSynthesizedStatementsMap != null && + _lazyGuardedSynthesizedStatementsMap.TryGetValue(node, out BoundStatement result)) + { + return result; + } + + return null; + } + private ImmutableArray GuardedGetBoundNodesFromMap(CSharpSyntaxNode node) { Debug.Assert(_nodeMapLock.IsWriteLockHeld || _nodeMapLock.IsReadLockHeld); @@ -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 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 { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs index de4052622dbd8..516846814c3f8 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs @@ -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 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().Single(); + var info = model.GetForEachStatementInfo(foreachStatement); + Assert.Equal("System.Object", info.ElementType.ToTestDisplayString()); + Assert.Equal("System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator()", + info.GetEnumeratorMethod.ToTestDisplayString()); + } } internal static class OutVarTestsExtensions diff --git a/src/EditorFeatures/CSharpTest/InlineDeclaration/CSharpInlineDeclarationTests.cs b/src/EditorFeatures/CSharpTest/InlineDeclaration/CSharpInlineDeclarationTests.cs index c156b994b2263..a479e16e889a3 100644 --- a/src/EditorFeatures/CSharpTest/InlineDeclaration/CSharpInlineDeclarationTests.cs +++ b/src/EditorFeatures/CSharpTest/InlineDeclaration/CSharpInlineDeclarationTests.cs @@ -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(