From 2c49c71081a36f984f171753e6fbf3d70f823fa8 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Thu, 11 Aug 2011 00:20:34 +0200 Subject: [PATCH] Report resolve results back to the navigator. --- .../CSharp/Analysis/ControlFlow.cs | 2 +- .../Analysis/DefiniteAssignmentAnalysis.cs | 2 +- .../CSharp/Resolver/AmbiguousResolveResult.cs | 4 +- .../Resolver/DetectSkippableNodesNavigator.cs | 80 +++++++++++++++++++ .../Resolver/IResolveVisitorNavigator.cs | 21 +++-- .../CSharp/Resolver/MemberLookup.cs | 2 +- .../NodeListResolveVisitorNavigator.cs | 4 + .../CSharp/Resolver/ResolveVisitor.cs | 60 ++++++++++---- .../ICSharpCode.NRefactory.csproj | 1 + NRefactory.sln | 2 +- 10 files changed, 143 insertions(+), 35 deletions(-) create mode 100644 ICSharpCode.NRefactory/CSharp/Resolver/DetectSkippableNodesNavigator.cs diff --git a/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs b/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs index 081bca7f35..5b371af588 100644 --- a/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs +++ b/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs @@ -167,7 +167,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis { return BuildControlFlowGraph(statement, new ResolveVisitor( new CSharpResolver(context, cancellationToken), - null, ConstantModeResolveVisitorNavigator.Skip)); + null)); } public IList BuildControlFlowGraph(Statement statement, ResolveVisitor resolveVisitor) diff --git a/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs b/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs index 70761e0cbc..900a2a967f 100644 --- a/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs +++ b/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs @@ -111,7 +111,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis public DefiniteAssignmentAnalysis(Statement rootStatement, ITypeResolveContext context, CancellationToken cancellationToken) : this(rootStatement, new ResolveVisitor(new CSharpResolver(context ?? MinimalResolveContext.Instance, cancellationToken), - null, ConstantModeResolveVisitorNavigator.Skip)) + null)) { } diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/AmbiguousResolveResult.cs b/ICSharpCode.NRefactory/CSharp/Resolver/AmbiguousResolveResult.cs index c4c39ef5aa..e8c4a20447 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/AmbiguousResolveResult.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/AmbiguousResolveResult.cs @@ -35,9 +35,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } - public class AmbiguousMemberResultResult : MemberResolveResult + public class AmbiguousMemberResolveResult : MemberResolveResult { - public AmbiguousMemberResultResult(ResolveResult targetResult, IMember member, IType returnType) : base(targetResult, member, returnType) + public AmbiguousMemberResolveResult(ResolveResult targetResult, IMember member, IType returnType) : base(targetResult, member, returnType) { } diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/DetectSkippableNodesNavigator.cs b/ICSharpCode.NRefactory/CSharp/Resolver/DetectSkippableNodesNavigator.cs new file mode 100644 index 0000000000..c310e5504d --- /dev/null +++ b/ICSharpCode.NRefactory/CSharp/Resolver/DetectSkippableNodesNavigator.cs @@ -0,0 +1,80 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// When an is searching for specific nodes + /// (e.g. all IdentifierExpressions), it has to scan the whole compilation unit for those nodes. + /// However, scanning in the ResolveVisitor is expensive (e.g. any lambda that is scanned must be resolved), + /// so it makes sense to detect when a whole subtree is scan-only, and skip that tree instead. + /// + /// The DetectSkippableNodesNavigator performs this job by running the input IResolveVisitorNavigator + /// over the whole AST, and detecting subtrees that are scan-only, and replaces them with Skip. + /// + public sealed class DetectSkippableNodesNavigator : IResolveVisitorNavigator + { + readonly Dictionary dict = new Dictionary(); + IResolveVisitorNavigator navigator; + + public DetectSkippableNodesNavigator(IResolveVisitorNavigator navigator, AstNode root) + { + this.navigator = navigator; + Init(root); + } + + bool Init(AstNode node) + { + var mode = navigator.Scan(node); + if (mode == ResolveVisitorNavigationMode.Skip) + return false; + + bool needsResolve = (mode != ResolveVisitorNavigationMode.Scan); + + for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { + needsResolve |= Init(child); + } + + if (needsResolve) { + // If this node or any child node needs resolving, store the mode in the dictionary. + dict.Add(node, mode); + } + return needsResolve; + } + + /// + public ResolveVisitorNavigationMode Scan(AstNode node) + { + ResolveVisitorNavigationMode mode; + if (dict.TryGetValue(node, out mode)) { + return mode; + } else { + return ResolveVisitorNavigationMode.Skip; + } + } + + /// + public void Resolved(AstNode node, ResolveResult result) + { + navigator.Resolved(node, result); + } + } +} diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/IResolveVisitorNavigator.cs b/ICSharpCode.NRefactory/CSharp/Resolver/IResolveVisitorNavigator.cs index 3e1f6a20b7..4b2d4cf7f3 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/IResolveVisitorNavigator.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/IResolveVisitorNavigator.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System; +using ICSharpCode.NRefactory.TypeSystem; namespace ICSharpCode.NRefactory.CSharp.Resolver { @@ -26,7 +27,15 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// public interface IResolveVisitorNavigator { + /// + /// Asks the navigator whether to scan, skip, or resolve a node. + /// ResolveVisitorNavigationMode Scan(AstNode node); + + /// + /// Notifies the navigator that a node was resolved. + /// + void Resolved(AstNode node, ResolveResult result); } /// @@ -52,16 +61,4 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// ResolveAll } - - sealed class ConstantModeResolveVisitorNavigator : IResolveVisitorNavigator - { - ResolveVisitorNavigationMode mode; - - public static readonly IResolveVisitorNavigator Skip = new ConstantModeResolveVisitorNavigator { mode = ResolveVisitorNavigationMode.Skip }; - - ResolveVisitorNavigationMode IResolveVisitorNavigator.Scan(AstNode node) - { - return mode; - } - } } diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/MemberLookup.cs b/ICSharpCode.NRefactory/CSharp/Resolver/MemberLookup.cs index 85e9016597..f83fa257ba 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/MemberLookup.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/MemberLookup.cs @@ -269,7 +269,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return new MemberResolveResult(targetResolveResult, firstNonMethod, context); if (firstNonMethod == null) return new MethodGroupResolveResult(targetResolveResult, name, members.ConvertAll(m => (IMethod)m), typeArguments); - return new AmbiguousMemberResultResult(targetResolveResult, firstNonMethod, firstNonMethod.ReturnType.Resolve(context)); + return new AmbiguousMemberResolveResult(targetResolveResult, firstNonMethod, firstNonMethod.ReturnType.Resolve(context)); } static bool IsNonInterfaceType(ITypeDefinition def) diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/NodeListResolveVisitorNavigator.cs b/ICSharpCode.NRefactory/CSharp/Resolver/NodeListResolveVisitorNavigator.cs index 1ceb46c725..43cccdadfe 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/NodeListResolveVisitorNavigator.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/NodeListResolveVisitorNavigator.cs @@ -54,5 +54,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return ResolveVisitorNavigationMode.Skip; } } + + void IResolveVisitorNavigator.Resolved(AstNode node, ResolveResult result) + { + } } } diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs b/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs index 63eec8a36d..5082e4a05e 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs @@ -168,7 +168,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver // lambdas must be resolved so that they get stored in the 'undecided' list only once goto case ResolveVisitorNavigationMode.Resolve; } - resolverBeforeDict[node] = resolver.Clone(); + StoreState(node, resolver.Clone()); node.AcceptVisitor(this, null); break; case ResolveVisitorNavigationMode.Resolve: @@ -192,9 +192,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver ResolveResult result; if (!resolveResultCache.TryGetValue(node, out result)) { resolver.cancellationToken.ThrowIfCancellationRequested(); - resolverBeforeDict[node] = resolver.Clone(); - result = resolveResultCache[node] = node.AcceptVisitor(this, null) ?? errorResult; + StoreState(node, resolver.Clone()); + result = node.AcceptVisitor(this, null) ?? errorResult; Log.WriteLine("Resolved '{0}' to {1}", node, result); + StoreResult(node, result); ProcessConversionsInResult(result); } if (wasScan) @@ -202,6 +203,20 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return result; } + void StoreState(AstNode node, CSharpResolver resolverState) + { + Debug.Assert(resolverState != null); + resolverBeforeDict.Add(node, resolverState); + } + + void StoreResult(AstNode node, ResolveResult result) + { + Debug.Assert(result != null); + resolveResultCache.Add(node, result); + if (navigator != null) + navigator.Resolved(node, result); + } + protected override ResolveResult VisitChildren(AstNode node, object data) { ScanChildren(node); @@ -1148,21 +1163,26 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver // rr = Resolve(memberReferenceExpression) IdentifierExpression identifierExpression = memberReferenceExpression.Target as IdentifierExpression; - if (identifierExpression != null && identifierExpression.TypeArguments.Count == 0) { + if (identifierExpression != null && identifierExpression.TypeArguments.Count == 0 + && !resolveResultCache.ContainsKey(identifierExpression)) + { // Special handling for §7.6.4.1 Identicial simple names and type names + StoreState(identifierExpression, resolver.Clone()); ResolveResult target = resolver.ResolveSimpleName(identifierExpression.Identifier, EmptyList.Instance); TypeResolveResult trr; if (IsVariableReferenceWithSameType(target, identifierExpression.Identifier, out trr)) { // It's ambiguous ResolveResult rr = ResolveMemberReferenceOnGivenTarget(target, memberReferenceExpression); - resolveResultCache[identifierExpression] = IsStaticResult(rr, null) ? trr : target; - Log.WriteLine("Ambiguous simple name '{0}' was resolved to {1}", - identifierExpression, resolveResultCache[identifierExpression]); + ResolveResult simpleNameRR = IsStaticResult(rr, null) ? trr : target; + Log.WriteLine("Ambiguous simple name '{0}' was resolved to {1}", identifierExpression, simpleNameRR); + StoreResult(identifierExpression, simpleNameRR); + ProcessConversionsInResult(simpleNameRR); return rr; } else { // It's not ambiguous - resolveResultCache[identifierExpression] = target; Log.WriteLine("Simple name '{0}' was resolved to {1}", identifierExpression, target); + StoreResult(identifierExpression, target); + ProcessConversionsInResult(target); if (resolverEnabled) { return ResolveMemberReferenceOnGivenTarget(target, memberReferenceExpression); } else { @@ -1203,24 +1223,30 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver MemberReferenceExpression mre = invocationExpression.Target as MemberReferenceExpression; IdentifierExpression identifierExpression = mre != null ? mre.Target as IdentifierExpression : null; - if (identifierExpression != null && identifierExpression.TypeArguments.Count == 0) { + if (identifierExpression != null && identifierExpression.TypeArguments.Count == 0 + && !resolveResultCache.ContainsKey(identifierExpression)) + { // Special handling for §7.6.4.1 Identicial simple names and type names ResolveResult idRR = resolver.ResolveSimpleName(identifierExpression.Identifier, EmptyList.Instance); ResolveResult target = ResolveMemberReferenceOnGivenTarget(idRR, mre); - resolveResultCache[mre] = target; Log.WriteLine("Member reference '{0}' on potentially-ambiguous simple-name was resolved to {1}", mre, target); + StoreResult(mre, target); + ProcessConversionsInResult(target); TypeResolveResult trr; if (IsVariableReferenceWithSameType(idRR, identifierExpression.Identifier, out trr)) { // It's ambiguous ResolveResult rr = ResolveInvocationOnGivenTarget(target, invocationExpression); - resolveResultCache[identifierExpression] = IsStaticResult(target, rr) ? trr : idRR; + ResolveResult simpleNameRR = IsStaticResult(target, rr) ? trr : idRR; Log.WriteLine("Ambiguous simple name '{0}' was resolved to {1}", - identifierExpression, resolveResultCache[identifierExpression]); + identifierExpression, simpleNameRR); + StoreResult(identifierExpression, simpleNameRR); + ProcessConversionsInResult(simpleNameRR); return rr; } else { // It's not ambiguous Log.WriteLine("Simple name '{0}' was resolved to {1}", identifierExpression, idRR); - resolveResultCache[identifierExpression] = idRR; + StoreResult(identifierExpression, idRR); + ProcessConversionsInResult(idRR); if (resolverEnabled) { return ResolveInvocationOnGivenTarget(target, invocationExpression); } else { @@ -1658,10 +1684,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver visitor.MergeUndecidedLambdas(); Log.WriteLine("Merging " + ToString()); foreach (var pair in visitor.resolveResultCache) { - parentVisitor.resolveResultCache.Add(pair.Key, pair.Value); + parentVisitor.StoreResult(pair.Key, pair.Value); } foreach (var pair in visitor.resolverBeforeDict) { - parentVisitor.resolverBeforeDict.Add(pair.Key, pair.Value); + parentVisitor.StoreState(pair.Key, pair.Value); } parentVisitor.undecidedLambdas.Remove(lambda); } @@ -2034,9 +2060,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver // so we just do it here and store the result in the resolver cache. IType actualType = typeRef.Resolve(resolver.Context); if (actualType.Kind != TypeKind.Unknown) { - resolveResultCache.Add(type, new TypeResolveResult(actualType)); + StoreResult(type, new TypeResolveResult(actualType)); } else { - resolveResultCache.Add(type, errorResult); + StoreResult(type, errorResult); } return actualType; } else { diff --git a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj index ed4753b060..cacee1a729 100644 --- a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj +++ b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj @@ -102,6 +102,7 @@ + diff --git a/NRefactory.sln b/NRefactory.sln index baf2f03d50..1a75afe989 100644 --- a/NRefactory.sln +++ b/NRefactory.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 -# SharpDevelop 4.1.0.7590-alpha +# SharpDevelop 4.1.0.7800-beta Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DC98210E-1646-483B-819A-2BB8272461E4}" ProjectSection(SolutionItems) = postProject README = README