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

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

@ -0,0 +1,42 @@ @@ -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 @@ @@ -3,8 +3,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
@ -16,22 +16,51 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -16,22 +16,51 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// <remarks>
/// The ResolveVisitor does two jobs at the same time: it tracks the resolve context (properties on CSharpResolver)
/// 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>
public class ResolveVisitor : AbstractDomVisitor<object, ResolveResult>
{
static readonly ResolveResult errorResult = new ErrorResolveResult(SharedTypes.UnknownType);
readonly CSharpResolver resolver;
CSharpResolver resolver;
readonly ParsedFile parsedFile;
readonly Dictionary<INode, ResolveResult> cache = new Dictionary<INode, ResolveResult>();
readonly IResolveVisitorNavigator navigator;
ResolveVisitorNavigationMode mode = ResolveVisitorNavigationMode.Scan;
bool resolverEnabled {
get { return mode != ResolveVisitorNavigationMode.Scan; }
}
#region Constructor
/// <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)
{
if (resolver == null)
@ -42,14 +71,19 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -42,14 +71,19 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (navigator == null)
mode = ResolveVisitorNavigationMode.ResolveAll;
}
#endregion
#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)
return;
if (mode == ResolveVisitorNavigationMode.ResolveAll) {
Resolve(node, data);
Resolve(node);
} else {
ResolveVisitorNavigationMode oldMode = mode;
mode = navigator.Scan(node);
@ -57,11 +91,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -57,11 +91,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
case ResolveVisitorNavigationMode.Skip:
break;
case ResolveVisitorNavigationMode.Scan:
node.AcceptVisitor(this, data);
node.AcceptVisitor(this, null);
break;
case ResolveVisitorNavigationMode.Resolve:
case ResolveVisitorNavigationMode.ResolveAll:
Resolve(node, data);
Resolve(node);
break;
default:
throw new Exception("Invalid value for ResolveVisitorNavigationMode");
@ -70,7 +104,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -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)
return errorResult;
@ -79,7 +113,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -79,7 +113,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
mode = ResolveVisitorNavigationMode.Resolve;
ResolveResult 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)
mode = ResolveVisitorNavigationMode.Scan;
@ -100,6 +134,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -100,6 +134,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
#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)
{
ResolveResult result;
@ -169,7 +207,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -169,7 +207,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{
int initializerCount = fieldDeclaration.Variables.Count();
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 (resolver.CurrentTypeDefinition != null) {
resolver.CurrentMember = resolver.CurrentTypeDefinition.Fields.FirstOrDefault(f => f.Region.IsInside(node.StartLocation));
@ -256,15 +294,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -256,15 +294,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
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) {
resolver.PushBlock();
try {
resolver.AddVariable(resolver.CurrentMember.ReturnType, "value");
Scan(node);
} finally {
resolver.PopBlock();
}
resolver.AddVariable(resolver.CurrentMember.ReturnType, "value");
Scan(node);
resolver.PopBlock();
} else {
Scan(node);
}
@ -282,6 +317,49 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -282,6 +317,49 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{
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
#region Track CheckForOverflow
@ -620,6 +698,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -620,6 +698,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
#endregion
#region Local Variable Scopes
public override ResolveResult VisitBlockStatement(BlockStatement blockStatement, object data)
{
resolver.PushBlock();
@ -628,43 +707,118 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -628,43 +707,118 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
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)
{
IType type = ResolveType(variableDeclarationStatement.ReturnType);
if (variableDeclarationStatement.Variables.Count() == 1) {
if (type == SharedTypes.UnknownType && IsVar(variableDeclarationStatement.ReturnType)) {
ResolveResult rr = Resolve(variableDeclarationStatement.Variables.Single().Initializer);
type = rr.Type;
bool isConst = (variableDeclarationStatement.Modifiers & Modifiers.Const) != 0;
VariableInitializer firstInitializer = variableDeclarationStatement.Variables.FirstOrDefault();
ITypeReference type = MakeTypeReference(variableDeclarationStatement.ReturnType, firstInitializer != null ? firstInitializer.Initializer : null);
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 {
foreach (VariableInitializer vi in variableDeclarationStatement.Variables)
Resolve(vi, type);
return null;
return SharedTypes.UnknownType; // TODO
}
}
bool IsVar(INode returnType)
static bool IsVar(INode returnType)
{
return returnType is IdentifierExpression && ((IdentifierExpression)returnType).Identifier == "var";
}
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));
}
}
sealed class VarTypeReference : ITypeReference
{
ResolveVisitor visitor;
CSharpResolver storedContext;
INode initializerExpression;
IType result;
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 @@ @@ -172,6 +172,7 @@
<Compile Include="CSharp\Resolver\MemberTypeOrNamespaceReference.cs" />
<Compile Include="CSharp\Resolver\MethodGroupResolveResult.cs" />
<Compile Include="CSharp\Resolver\NamespaceResolveResult.cs" />
<Compile Include="CSharp\Resolver\NodeListResolveVisitorNavigator.cs" />
<Compile Include="CSharp\Resolver\OverloadResolution.cs" />
<Compile Include="CSharp\Resolver\OverloadResolutionErrors.cs" />
<Compile Include="CSharp\Resolver\ResolveResult.cs" />

Loading…
Cancel
Save