Browse Source

Implemented local variable type inference.

newNRvisualizers
Daniel Grunwald 15 years ago
parent
commit
e64faf10b7
  1. 8
      ICSharpCode.NRefactory/CSharp/Resolver/IResolveVisitorNavigator.cs
  2. 42
      ICSharpCode.NRefactory/CSharp/Resolver/NodeListResolveVisitorNavigator.cs
  3. 244
      ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs
  4. 1
      ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

8
ICSharpCode.NRefactory/CSharp/Resolver/IResolveVisitorNavigator.cs

@ -5,11 +5,19 @@ using System;
namespace ICSharpCode.NRefactory.CSharp.Resolver namespace ICSharpCode.NRefactory.CSharp.Resolver
{ {
/// <summary>
/// Allows controlling which nodes are resolved by the resolve visitor.
/// </summary>
/// <seealso cref="ResolveVisitor"/>
public interface IResolveVisitorNavigator public interface IResolveVisitorNavigator
{ {
ResolveVisitorNavigationMode Scan(INode node); ResolveVisitorNavigationMode Scan(INode node);
} }
/// <summary>
/// Represents the operation mode of the resolve visitor.
/// </summary>
/// <seealso cref="ResolveVisitor"/>
public enum ResolveVisitorNavigationMode public enum ResolveVisitorNavigationMode
{ {
/// <summary> /// <summary>

42
ICSharpCode.NRefactory/CSharp/Resolver/NodeListResolveVisitorNavigator.cs

@ -0,0 +1,42 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
namespace ICSharpCode.NRefactory.CSharp.Resolver
{
/// <summary>
/// <see cref="IResolveVisitorNavigator"/> implementation that resolves a list of nodes.
/// We will skip all nodes which are not the target nodes or ancestors of the target nodes.
/// </summary>
public sealed class NodeListResolveVisitorNavigator : IResolveVisitorNavigator
{
readonly Dictionary<INode, ResolveVisitorNavigationMode> dict = new Dictionary<INode, ResolveVisitorNavigationMode>();
/// <summary>
/// Creates a new NodeListResolveVisitorNavigator that resolves the specified nodes.
/// </summary>
public NodeListResolveVisitorNavigator(IEnumerable<INode> nodes)
{
if (nodes == null)
throw new ArgumentNullException("nodes");
foreach (INode node in nodes) {
dict[node] = ResolveVisitorNavigationMode.Resolve;
for (INode ancestor = node.Parent; ancestor != null && !dict.ContainsKey(ancestor); ancestor = ancestor.Parent) {
dict.Add(ancestor, ResolveVisitorNavigationMode.Scan);
}
}
}
/// <inheritdoc/>
public ResolveVisitorNavigationMode Scan(INode node)
{
ResolveVisitorNavigationMode mode;
if (dict.TryGetValue(node, out mode))
return mode;
else
return ResolveVisitorNavigationMode.Skip;
}
}
}

244
ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs

@ -3,8 +3,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation; using ICSharpCode.NRefactory.TypeSystem.Implementation;
@ -16,22 +16,51 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// <remarks> /// <remarks>
/// The ResolveVisitor does two jobs at the same time: it tracks the resolve context (properties on CSharpResolver) /// The ResolveVisitor does two jobs at the same time: it tracks the resolve context (properties on CSharpResolver)
/// and it resolves the expressions visited. /// and it resolves the expressions visited.
/// To allow using the context tracking without having to resolve every expression in the file (e.g. /// To allow using the context tracking without having to resolve every expression in the file (e.g. when you want to resolve
/// only a single node deep within the DOM), you can use the <see cref="IResolveVisitorNavigator"/> interface.
/// The navigator allows you to switch the between scanning mode and resolving mode.
/// In scanning mode, the context is tracked (local variables registered etc.), but nodes are not resolved.
/// While scanning, the navigator will get asked about every node that the resolve visitor is about to enter.
/// This allows the navigator whether to keep scanning, whether switch to resolving mode, or whether to completely skip the
/// subtree rooted at that node.
///
/// In resolving mode, the context is tracked and nodes will be resolved.
/// The resolve visitor may decide that it needs to resolve other nodes as well in order to resolve the current node.
/// In this case, those nodes will be resolved automatically, without asking the navigator interface.
/// For child nodes that are not essential to resolving, the resolve visitor will switch back to scanning mode (and thus will
/// ask the navigator for further instructions).
///
/// Moreover, there is the <c>ResolveAll</c> mode - it works similar to resolving mode, but will not switch back to scanning mode.
/// The whole subtree will be resolved without notifying the navigator.
/// </remarks> /// </remarks>
public class ResolveVisitor : AbstractDomVisitor<object, ResolveResult> public class ResolveVisitor : AbstractDomVisitor<object, ResolveResult>
{ {
static readonly ResolveResult errorResult = new ErrorResolveResult(SharedTypes.UnknownType); static readonly ResolveResult errorResult = new ErrorResolveResult(SharedTypes.UnknownType);
readonly CSharpResolver resolver; CSharpResolver resolver;
readonly ParsedFile parsedFile; readonly ParsedFile parsedFile;
readonly Dictionary<INode, ResolveResult> cache = new Dictionary<INode, ResolveResult>(); readonly Dictionary<INode, ResolveResult> cache = new Dictionary<INode, ResolveResult>();
readonly IResolveVisitorNavigator navigator; readonly IResolveVisitorNavigator navigator;
ResolveVisitorNavigationMode mode = ResolveVisitorNavigationMode.Scan; ResolveVisitorNavigationMode mode = ResolveVisitorNavigationMode.Scan;
bool resolverEnabled { #region Constructor
get { return mode != ResolveVisitorNavigationMode.Scan; } /// <summary>
} /// Creates a new ResolveVisitor instance.
/// </summary>
/// <param name="resolver">
/// The CSharpResolver, describing the initial resolve context.
/// If you visit a whole CompilationUnit with the resolve visitor, you can simply pass
/// <c>new CSharpResolver(typeResolveContext)</c> without setting up the context.
/// If you only visit a subtree, you need to pass a CSharpResolver initialized to the context for that subtree.
/// </param>
/// <param name="parsedFile">
/// Result of the <see cref="TypeSystemConvertVisitor"/> for the file being passed. This is used for setting up the context on the resolver.
/// You may pass <c>null</c> if you are only visiting a part of a method body and have already set up the context in the <paramref name="resolver"/>.
/// </param>
/// <param name="navigator">
/// The navigator, which controls where the resolve visitor will switch between scanning mode and resolving mode.
/// If you pass <c>null</c>, then <c>ResolveAll</c> mode will be used.
/// </param>
public ResolveVisitor(CSharpResolver resolver, ParsedFile parsedFile, IResolveVisitorNavigator navigator = null) public ResolveVisitor(CSharpResolver resolver, ParsedFile parsedFile, IResolveVisitorNavigator navigator = null)
{ {
if (resolver == null) if (resolver == null)
@ -42,14 +71,19 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (navigator == null) if (navigator == null)
mode = ResolveVisitorNavigationMode.ResolveAll; mode = ResolveVisitorNavigationMode.ResolveAll;
} }
#endregion
#region Scan / Resolve #region Scan / Resolve
public void Scan(INode node, object data = null) bool resolverEnabled {
get { return mode != ResolveVisitorNavigationMode.Scan; }
}
public void Scan(INode node)
{ {
if (node == null) if (node == null)
return; return;
if (mode == ResolveVisitorNavigationMode.ResolveAll) { if (mode == ResolveVisitorNavigationMode.ResolveAll) {
Resolve(node, data); Resolve(node);
} else { } else {
ResolveVisitorNavigationMode oldMode = mode; ResolveVisitorNavigationMode oldMode = mode;
mode = navigator.Scan(node); mode = navigator.Scan(node);
@ -57,11 +91,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
case ResolveVisitorNavigationMode.Skip: case ResolveVisitorNavigationMode.Skip:
break; break;
case ResolveVisitorNavigationMode.Scan: case ResolveVisitorNavigationMode.Scan:
node.AcceptVisitor(this, data); node.AcceptVisitor(this, null);
break; break;
case ResolveVisitorNavigationMode.Resolve: case ResolveVisitorNavigationMode.Resolve:
case ResolveVisitorNavigationMode.ResolveAll: case ResolveVisitorNavigationMode.ResolveAll:
Resolve(node, data); Resolve(node);
break; break;
default: default:
throw new Exception("Invalid value for ResolveVisitorNavigationMode"); throw new Exception("Invalid value for ResolveVisitorNavigationMode");
@ -70,7 +104,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
} }
} }
public ResolveResult Resolve(INode node, object data = null) public ResolveResult Resolve(INode node)
{ {
if (node == null) if (node == null)
return errorResult; return errorResult;
@ -79,7 +113,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
mode = ResolveVisitorNavigationMode.Resolve; mode = ResolveVisitorNavigationMode.Resolve;
ResolveResult result; ResolveResult result;
if (!cache.TryGetValue(node, out result)) { if (!cache.TryGetValue(node, out result)) {
result = cache[node] = node.AcceptVisitor(this, data) ?? errorResult; result = cache[node] = node.AcceptVisitor(this, null) ?? errorResult;
} }
if (wasScan) if (wasScan)
mode = ResolveVisitorNavigationMode.Scan; mode = ResolveVisitorNavigationMode.Scan;
@ -100,6 +134,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
} }
#endregion #endregion
/// <summary>
/// Gets the cached resolve result for the specified node.
/// Returns <c>null</c> if no cached result was found (e.g. if the node was not visited; or if it was visited in scanning mode).
/// </summary>
public ResolveResult GetResolveResult(INode node) public ResolveResult GetResolveResult(INode node)
{ {
ResolveResult result; ResolveResult result;
@ -169,7 +207,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{ {
int initializerCount = fieldDeclaration.Variables.Count(); int initializerCount = fieldDeclaration.Variables.Count();
ResolveResult result = null; ResolveResult result = null;
foreach (INode node in fieldDeclaration.Children) { for (INode node = fieldDeclaration.FirstChild; node != null; node = node.NextSibling) {
if (node.Role == FieldDeclaration.Roles.Initializer) { if (node.Role == FieldDeclaration.Roles.Initializer) {
if (resolver.CurrentTypeDefinition != null) { if (resolver.CurrentTypeDefinition != null) {
resolver.CurrentMember = resolver.CurrentTypeDefinition.Fields.FirstOrDefault(f => f.Region.IsInside(node.StartLocation)); resolver.CurrentMember = resolver.CurrentTypeDefinition.Fields.FirstOrDefault(f => f.Region.IsInside(node.StartLocation));
@ -256,15 +294,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
resolver.CurrentMember = resolver.CurrentTypeDefinition.Properties.FirstOrDefault(p => p.Region.IsInside(propertyDeclaration.StartLocation)); resolver.CurrentMember = resolver.CurrentTypeDefinition.Properties.FirstOrDefault(p => p.Region.IsInside(propertyDeclaration.StartLocation));
} }
foreach (INode node in propertyDeclaration.Children) { for (INode node = propertyDeclaration.FirstChild; node != null; node = node.NextSibling) {
if (node.Role == PropertyDeclaration.PropertySetRole && resolver.CurrentMember != null) { if (node.Role == PropertyDeclaration.PropertySetRole && resolver.CurrentMember != null) {
resolver.PushBlock(); resolver.PushBlock();
try { resolver.AddVariable(resolver.CurrentMember.ReturnType, "value");
resolver.AddVariable(resolver.CurrentMember.ReturnType, "value"); Scan(node);
Scan(node); resolver.PopBlock();
} finally {
resolver.PopBlock();
}
} else { } else {
Scan(node); Scan(node);
} }
@ -282,6 +317,49 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{ {
return VisitPropertyDeclaration(indexerDeclaration, data); return VisitPropertyDeclaration(indexerDeclaration, data);
} }
public override ResolveResult VisitEventDeclaration(EventDeclaration eventDeclaration, object data)
{
try {
if (resolver.CurrentTypeDefinition != null) {
resolver.CurrentMember = resolver.CurrentTypeDefinition.Events.FirstOrDefault(e => e.Region.IsInside(eventDeclaration.StartLocation));
}
if (resolver.CurrentMember != null) {
resolver.PushBlock();
resolver.AddVariable(resolver.CurrentMember.ReturnType, "value");
ScanChildren(eventDeclaration);
resolver.PopBlock();
} else {
ScanChildren(eventDeclaration);
}
if (resolverEnabled && resolver.CurrentMember != null)
return new MemberResolveResult(resolver.CurrentMember, resolver.CurrentMember.ReturnType.Resolve(resolver.Context));
else
return errorResult;
} finally {
resolver.CurrentMember = null;
}
}
public override ResolveResult VisitParameterDeclaration(ParameterDeclaration parameterDeclaration, object data)
{
ScanChildren(parameterDeclaration);
if (resolverEnabled) {
IParameterizedMember pm = resolver.CurrentMember as IParameterizedMember;
if (pm != null) {
foreach (IParameter p in pm.Parameters) {
if (p.Name == parameterDeclaration.Name) {
return new VariableResolveResult(p, p.Type.Resolve(resolver.Context));
}
}
}
return errorResult;
} else {
return null;
}
}
#endregion #endregion
#region Track CheckForOverflow #region Track CheckForOverflow
@ -620,6 +698,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
} }
#endregion #endregion
#region Local Variable Scopes
public override ResolveResult VisitBlockStatement(BlockStatement blockStatement, object data) public override ResolveResult VisitBlockStatement(BlockStatement blockStatement, object data)
{ {
resolver.PushBlock(); resolver.PushBlock();
@ -628,43 +707,118 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
return null; return null;
} }
public override ResolveResult VisitUsingStatement(UsingStatement usingStatement, object data)
{
resolver.PushBlock();
ScanChildren(usingStatement);
resolver.PopBlock();
return null;
}
public override ResolveResult VisitForStatement(ForStatement forStatement, object data)
{
resolver.PushBlock();
ScanChildren(forStatement);
resolver.PopBlock();
return null;
}
#endregion
#region VariableDeclarationStatement
public override ResolveResult VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, object data) public override ResolveResult VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, object data)
{ {
IType type = ResolveType(variableDeclarationStatement.ReturnType); bool isConst = (variableDeclarationStatement.Modifiers & Modifiers.Const) != 0;
if (variableDeclarationStatement.Variables.Count() == 1) { VariableInitializer firstInitializer = variableDeclarationStatement.Variables.FirstOrDefault();
if (type == SharedTypes.UnknownType && IsVar(variableDeclarationStatement.ReturnType)) { ITypeReference type = MakeTypeReference(variableDeclarationStatement.ReturnType, firstInitializer != null ? firstInitializer.Initializer : null);
ResolveResult rr = Resolve(variableDeclarationStatement.Variables.Single().Initializer);
type = rr.Type; int initializerCount = variableDeclarationStatement.Variables.Count();
ResolveResult result = null;
for (INode node = variableDeclarationStatement.FirstChild; node != null; node = node.NextSibling) {
if (node.Role == FieldDeclaration.Roles.Initializer) {
VariableInitializer vi = (VariableInitializer)node;
IConstantValue cv = null;
if (isConst)
throw new NotImplementedException();
resolver.AddVariable(type, vi.Name, cv);
if (resolverEnabled && initializerCount == 1) {
result = Resolve(node);
} else {
Scan(node);
}
} else {
Scan(node);
} }
return Resolve(variableDeclarationStatement.Variables.Single(), type); }
return result;
}
#endregion
#region Local Variable Type Inference
/// <summary>
/// Creates a type reference for the specified type node.
/// If the type node is 'var', performs type inference on the initializer expression.
/// </summary>
ITypeReference MakeTypeReference(INode type, INode initializerExpression)
{
if (initializerExpression != null && IsVar(type)) {
return new VarTypeReference(this, resolver.Clone(), initializerExpression);
} else { } else {
foreach (VariableInitializer vi in variableDeclarationStatement.Variables) return SharedTypes.UnknownType; // TODO
Resolve(vi, type);
return null;
} }
} }
bool IsVar(INode returnType) static bool IsVar(INode returnType)
{ {
return returnType is IdentifierExpression && ((IdentifierExpression)returnType).Identifier == "var"; return returnType is IdentifierExpression && ((IdentifierExpression)returnType).Identifier == "var";
} }
public override ResolveResult VisitParameterDeclaration(ParameterDeclaration parameterDeclaration, object data) sealed class VarTypeReference : ITypeReference
{ {
ScanChildren(parameterDeclaration); ResolveVisitor visitor;
if (resolverEnabled) { CSharpResolver storedContext;
IParameterizedMember pm = resolver.CurrentMember as IParameterizedMember; INode initializerExpression;
if (pm != null) {
foreach (IParameter p in pm.Parameters) { IType result;
if (p.Name == parameterDeclaration.Name) {
return new VariableResolveResult(p, p.Type.Resolve(resolver.Context)); public VarTypeReference(ResolveVisitor visitor, CSharpResolver storedContext, INode initializerExpression)
} {
} this.visitor = visitor;
this.storedContext = storedContext;
this.initializerExpression = initializerExpression;
}
public IType Resolve(ITypeResolveContext context)
{
if (visitor == null)
return result ?? SharedTypes.UnknownType;
var oldMode = visitor.mode;
var oldResolver = visitor.resolver;
try {
visitor.mode = ResolveVisitorNavigationMode.Resolve;
visitor.resolver = storedContext;
return result = visitor.Resolve(initializerExpression).Type;
} finally {
visitor.mode = oldMode;
visitor.resolver = oldResolver;
visitor = null;
storedContext = null;
initializerExpression = null;
} }
return errorResult; }
} else {
return null; public override string ToString()
{
if (visitor == null)
return "var=" + result;
else
return "var (not yet resolved)";
} }
} }
#endregion
} }
} }

1
ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

@ -172,6 +172,7 @@
<Compile Include="CSharp\Resolver\MemberTypeOrNamespaceReference.cs" /> <Compile Include="CSharp\Resolver\MemberTypeOrNamespaceReference.cs" />
<Compile Include="CSharp\Resolver\MethodGroupResolveResult.cs" /> <Compile Include="CSharp\Resolver\MethodGroupResolveResult.cs" />
<Compile Include="CSharp\Resolver\NamespaceResolveResult.cs" /> <Compile Include="CSharp\Resolver\NamespaceResolveResult.cs" />
<Compile Include="CSharp\Resolver\NodeListResolveVisitorNavigator.cs" />
<Compile Include="CSharp\Resolver\OverloadResolution.cs" /> <Compile Include="CSharp\Resolver\OverloadResolution.cs" />
<Compile Include="CSharp\Resolver\OverloadResolutionErrors.cs" /> <Compile Include="CSharp\Resolver\OverloadResolutionErrors.cs" />
<Compile Include="CSharp\Resolver\ResolveResult.cs" /> <Compile Include="CSharp\Resolver\ResolveResult.cs" />

Loading…
Cancel
Save