Skip to content
This repository has been archived by the owner on Sep 24, 2020. It is now read-only.

Commit

Permalink
Fix #18: ResolveResult for object creation
Browse files Browse the repository at this point in the history
  • Loading branch information
dgrunwald committed Mar 16, 2012
1 parent ab024b9 commit d338acc
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,10 @@ public class CSharpInvocationResolveResult : InvocationResolveResult
bool isExtensionMethodInvocation = false,
bool isExpandedForm = false,
bool isDelegateInvocation = false,
IList<int> argumentToParameterMap = null)
: base(targetResult, member, arguments)
IList<int> argumentToParameterMap = null,
IList<ResolveResult> initializerStatements = null
)
: base(targetResult, member, arguments, initializerStatements)
{
this.OverloadResolutionErrors = overloadResolutionErrors;
this.IsExtensionMethodInvocation = isExtensionMethodInvocation;
Expand Down
47 changes: 34 additions & 13 deletions ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -278,12 +278,12 @@ public CSharpResolver PopLastVariable()
#region Object Initializer Context
sealed class ObjectInitializerContext
{
internal readonly IType type;
internal readonly ResolveResult initializedObject;
internal readonly ObjectInitializerContext prev;

public ObjectInitializerContext(IType type, CSharpResolver.ObjectInitializerContext prev)
public ObjectInitializerContext(ResolveResult initializedObject, CSharpResolver.ObjectInitializerContext prev)
{
this.type = type;
this.initializedObject = initializedObject;
this.prev = prev;
}
}
Expand All @@ -298,27 +298,45 @@ CSharpResolver WithObjectInitializerStack(ObjectInitializerContext stack)
/// <summary>
/// Pushes the type of the object that is currently being initialized.
/// </summary>
public CSharpResolver PushInitializerType(IType type)
public CSharpResolver PushObjectInitializer(ResolveResult initializedObject)
{
if (type == null)
throw new ArgumentNullException("type");
return WithObjectInitializerStack(new ObjectInitializerContext(type, objectInitializerStack));
if (initializedObject == null)
throw new ArgumentNullException("initializedObject");
return WithObjectInitializerStack(new ObjectInitializerContext(initializedObject, objectInitializerStack));
}

public CSharpResolver PopInitializerType()
public CSharpResolver PopObjectInitializer()
{
if (objectInitializerStack == null)
throw new InvalidOperationException();
return WithObjectInitializerStack(objectInitializerStack.prev);
}

/// <summary>
/// Gets whether this context is within an object initializer.
/// </summary>
public bool IsInObjectInitializer {
get { return objectInitializerStack != null; }
}

/// <summary>
/// Gets the current object initializer. This usually is an <see cref="InitializedObjectResolveResult"/>
/// or (for nested initializers) a semantic tree based on an <see cref="InitializedObjectResolveResult"/>.
/// Returns ErrorResolveResult if there is no object initializer.
/// </summary>
public ResolveResult CurrentObjectInitializer {
get {
return objectInitializerStack != null ? objectInitializerStack.initializedObject : ErrorResult;
}
}

/// <summary>
/// Gets the type of the object currently being initialized.
/// Returns SharedTypes.Unknown if no object initializer is currently open (or if the object initializer
/// has unknown type).
/// </summary>
public IType CurrentObjectInitializerType {
get { return objectInitializerStack != null ? objectInitializerStack.type : SpecialType.UnknownType; }
get { return CurrentObjectInitializer.Type; }
}
#endregion

Expand Down Expand Up @@ -1644,8 +1662,7 @@ public MemberLookup CreateMemberLookup()
public ResolveResult ResolveIdentifierInObjectInitializer(string identifier)
{
MemberLookup memberLookup = CreateMemberLookup();
ResolveResult target = new ResolveResult(this.CurrentObjectInitializerType);
return memberLookup.Lookup(target, identifier, EmptyList<IType>.Instance, false);
return memberLookup.Lookup(this.CurrentObjectInitializer, identifier, EmptyList<IType>.Instance, false);
}
#endregion

Expand Down Expand Up @@ -2025,8 +2042,12 @@ void AdjustArrayAccessArguments(ResolveResult[] arguments)
/// Whether to allow calling protected constructors.
/// This should be false except when resolving constructor initializers.
/// </param>
/// <param name="initializerStatements">
/// Statements for Objects/Collections initializer.
/// <see cref="InvocationResolveResult.InitializerStatements"/>
/// </param>
/// <returns>InvocationResolveResult or ErrorResolveResult</returns>
public ResolveResult ResolveObjectCreation(IType type, ResolveResult[] arguments, string[] argumentNames = null, bool allowProtectedAccess = false)
public ResolveResult ResolveObjectCreation(IType type, ResolveResult[] arguments, string[] argumentNames = null, bool allowProtectedAccess = false, IList<ResolveResult> initializerStatements = null)
{
if (type.Kind == TypeKind.Delegate && arguments.Length == 1) {
return Convert(arguments[0], type);
Expand All @@ -2040,7 +2061,7 @@ public ResolveResult ResolveObjectCreation(IType type, ResolveResult[] arguments
or.AddCandidate(ctor, OverloadResolutionErrors.Inaccessible);
}
if (or.BestCandidate != null) {
return or.CreateResolveResult(null);
return or.CreateResolveResult(null, initializerStatements);
} else {
return new ErrorResolveResult(type);
}
Expand Down
15 changes: 13 additions & 2 deletions ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,17 @@ public IParameterizedMember GetBestCandidateWithSubstitutedTypeArguments()
}
}

public CSharpInvocationResolveResult CreateResolveResult(ResolveResult targetResolveResult)
/// <summary>
/// Creates a ResolveResult representing the result of overload resolution.
/// </summary>
/// <param name="targetResolveResult">
/// The target expression of the call. May be <c>null</c> for static methods/constructors.
/// </param>
/// <param name="initializerStatements">
/// Statements for Objects/Collections initializer.
/// <see cref="InvocationResolveResult.InitializerStatements"/>
/// </param>
public CSharpInvocationResolveResult CreateResolveResult(ResolveResult targetResolveResult, IList<ResolveResult> initializerStatements = null)
{
IParameterizedMember member = GetBestCandidateWithSubstitutedTypeArguments();
if (member == null)
Expand All @@ -827,7 +837,8 @@ public CSharpInvocationResolveResult CreateResolveResult(ResolveResult targetRes
this.IsExtensionMethodInvocation,
this.BestCandidateIsExpandedForm,
isDelegateInvocation: false,
argumentToParameterMap: this.GetArgumentToParameterMap());
argumentToParameterMap: this.GetArgumentToParameterMap(),
initializerStatements: initializerStatements);
}
}
}
63 changes: 38 additions & 25 deletions ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1275,21 +1275,30 @@ ResolveResult IAstVisitor<ResolveResult>.VisitNamedArgumentExpression(NamedArgum
// NamedExpression is "identifier = Expression" in object initializers and attributes
ResolveResult IAstVisitor<ResolveResult>.VisitNamedExpression(NamedExpression namedExpression)
{
// The parent expression takes care of handling NamedExpression
// by calling HandleObjectInitializer() or HandleNamedExpression().
// This method gets called only when scanning, or when the named expression is used
// in an invalid context.
ScanChildren(namedExpression);
return null;
}

void HandleNamedExpression(NamedExpression namedExpression, List<ResolveResult> initializerStatements)
{
StoreCurrentState(namedExpression);
Expression rhs = namedExpression.Expression;
ResolveResult lhsRR = resolver.ResolveIdentifierInObjectInitializer(namedExpression.Identifier);
if (rhs is ArrayInitializerExpression) {
ResolveResult result = resolver.ResolveIdentifierInObjectInitializer(namedExpression.Identifier);
HandleObjectInitializer(result.Type, (ArrayInitializerExpression)rhs);
return result;
HandleObjectInitializer(lhsRR, (ArrayInitializerExpression)rhs, initializerStatements);
} else {
if (resolverEnabled) {
ResolveResult result = resolver.ResolveIdentifierInObjectInitializer(namedExpression.Identifier);
ResolveAndProcessConversion(rhs, result.Type);
return result;
} else {
ScanChildren(namedExpression);
return null;
var rhsRR = Resolve(rhs);
var rr = resolver.ResolveAssignment(AssignmentOperatorType.Assign, lhsRR, rhsRR) as OperatorResolveResult;
if (rr != null) {
ProcessConversionResult(rhs, rr.Operands[1] as ConversionResolveResult);
initializerStatements.Add(rr);
}
}
StoreResult(namedExpression, lhsRR);
}

ResolveResult IAstVisitor<ResolveResult>.VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression)
Expand All @@ -1305,15 +1314,17 @@ ResolveResult IAstVisitor<ResolveResult>.VisitObjectCreateExpression(ObjectCreat
return typeResolveResult;
IType type = typeResolveResult.Type;

List<ResolveResult> initializerStatements = null;
var initializer = objectCreateExpression.Initializer;
if (!initializer.IsNull) {
HandleObjectInitializer(type, initializer);
initializerStatements = new List<ResolveResult>();
HandleObjectInitializer(new InitializedObjectResolveResult(type), initializer, initializerStatements);
}

string[] argumentNames;
ResolveResult[] arguments = GetArguments(objectCreateExpression.Arguments, out argumentNames);

ResolveResult rr = resolver.ResolveObjectCreation(type, arguments, argumentNames);
ResolveResult rr = resolver.ResolveObjectCreation(type, arguments, argumentNames, false, initializerStatements);
if (arguments.Length == 1 && rr.Type.Kind == TypeKind.Delegate) {
// process conversion in case it's a delegate creation
ProcessConversionResult(objectCreateExpression.Arguments.Single(), rr as ConversionResolveResult);
Expand All @@ -1332,10 +1343,10 @@ ResolveResult IAstVisitor<ResolveResult>.VisitObjectCreateExpression(ObjectCreat
}
}

void HandleObjectInitializer(IType type, ArrayInitializerExpression initializer)
void HandleObjectInitializer(ResolveResult initializedObject, ArrayInitializerExpression initializer, List<ResolveResult> initializerStatements)
{
StoreCurrentState(initializer);
resolver = resolver.PushInitializerType(type);
resolver = resolver.PushObjectInitializer(initializedObject);
foreach (Expression element in initializer.Elements) {
ArrayInitializerExpression aie = element as ArrayInitializerExpression;
if (aie != null) {
Expand All @@ -1347,24 +1358,25 @@ void HandleObjectInitializer(IType type, ArrayInitializerExpression initializer)
addArguments[i++] = Resolve(addArgument);
}
MemberLookup memberLookup = resolver.CreateMemberLookup();
ResolveResult targetResult = new ResolveResult(type);
var addRR = memberLookup.Lookup(targetResult, "Add", EmptyList<IType>.Instance, true);
var addRR = memberLookup.Lookup(initializedObject, "Add", EmptyList<IType>.Instance, true);
var mgrr = addRR as MethodGroupResolveResult;
if (mgrr != null) {
OverloadResolution or = mgrr.PerformOverloadResolution(resolver.Compilation, addArguments, null, false, false, resolver.conversions);
var invocationRR = or.CreateResolveResult(targetResult);
var invocationRR = or.CreateResolveResult(initializedObject);
StoreResult(aie, invocationRR);
ProcessConversionsInInvocation(null, aie.Elements, invocationRR);
initializerStatements.Add(invocationRR);
} else {
StoreResult(aie, addRR);
}
} else if (element is NamedExpression) {
HandleNamedExpression((NamedExpression)element, initializerStatements);
} else {
// assignment in object initializer (NamedExpression),
// or some unknown kind of expression
// unknown kind of expression
Scan(element);
}
}
resolver = resolver.PopInitializerType();
resolver = resolver.PopObjectInitializer();
StoreResult(initializer, voidResult);
}

Expand Down Expand Up @@ -2927,18 +2939,19 @@ ResolveResult IAstVisitor<ResolveResult>.VisitAttribute(Attribute attribute)

// Separate arguments into ctor arguments and non-ctor arguments:
var constructorArguments = attribute.Arguments.Where(a => !(a is NamedExpression));
var nonConstructorArguments = attribute.Arguments.Where(a => a is NamedExpression);
var nonConstructorArguments = attribute.Arguments.OfType<NamedExpression>();

// Scan the non-constructor arguments
resolver = resolver.PushInitializerType(type);
resolver = resolver.PushObjectInitializer(new InitializedObjectResolveResult(type));
List<ResolveResult> initializerStatements = new List<ResolveResult>();
foreach (var arg in nonConstructorArguments)
Scan(arg);
resolver = resolver.PopInitializerType();
HandleNamedExpression(arg, initializerStatements);
resolver = resolver.PopObjectInitializer();

// Resolve the ctor arguments and find the matching ctor overload
string[] argumentNames;
ResolveResult[] arguments = GetArguments(constructorArguments, out argumentNames);
ResolveResult rr = resolver.ResolveObjectCreation(type, arguments, argumentNames);
ResolveResult rr = resolver.ResolveObjectCreation(type, arguments, argumentNames, false, initializerStatements);
ProcessConversionsInInvocation(null, constructorArguments, rr as CSharpInvocationResolveResult);
return rr;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE.

using System;
using System.Collections.Generic;
using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.TypeSystem;
using NUnit.Framework;
Expand Down Expand Up @@ -233,5 +234,31 @@ class B : A { protected B(int y) {} }";
Assert.AreEqual(OverloadResolutionErrors.Inaccessible, result.OverloadResolutionErrors);
Assert.AreEqual("B..ctor", result.Member.FullName); // should still find member even if it's not accessible
}

[Test]
public void ComplexObjectInitializer()
{
string program = @"using System;
using System.Collections.Generic;
struct Point { public int X, Y; }
class Test {
public Point Pos;
public List<string> List = new List<string>();
public Dictionary<string, int> Dict = new Dictionary<string, int>();
static object M() {
return $new Test {
Pos = { X = 1, Y = 2 },
List = { ""Hello"", ""World"" },
Dict = { { ""A"", 1 } }
}$;
}
}";
var rr = Resolve<CSharpInvocationResolveResult>(program);
Assert.IsFalse(rr.IsError);
Assert.AreEqual("Test..ctor", rr.Member.FullName);
Assert.AreEqual("Test", rr.Type.ReflectionName);
Assert.AreEqual(5, rr.InitializerStatements.Count);
}
}
}
1 change: 1 addition & 0 deletions ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
<Compile Include="Semantics\ErrorResolveResult.cs" />
<Compile Include="Semantics\Conversion.cs" />
<Compile Include="Semantics\ForEachResolveResult.cs" />
<Compile Include="Semantics\InitializedObjectResolveResult.cs" />
<Compile Include="Semantics\InvocationResolveResult.cs" />
<Compile Include="Semantics\LocalResolveResult.cs" />
<Compile Include="Semantics\MemberResolveResult.cs" />
Expand Down
34 changes: 34 additions & 0 deletions ICSharpCode.NRefactory/Semantics/InitializedObjectResolveResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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 ICSharpCode.NRefactory.TypeSystem;

namespace ICSharpCode.NRefactory.Semantics
{
/// <summary>
/// Refers to the object that is currently being initialized.
/// Used within <see cref="InvocationResolveResult.InitializerStatements"/>.
/// </summary>
public class InitializedObjectResolveResult : ResolveResult
{
public InitializedObjectResolveResult(IType type) : base(type)
{
}
}
}
17 changes: 14 additions & 3 deletions ICSharpCode.NRefactory/Semantics/InvocationResolveResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,28 @@ namespace ICSharpCode.NRefactory.Semantics
/// <summary>
/// Represents the result of a method invocation.
/// </summary>
public abstract class InvocationResolveResult : MemberResolveResult
public class InvocationResolveResult : MemberResolveResult
{
/// <summary>
/// Gets the arguments that are being passed to the method, in the order the arguments are being evaluated.
/// </summary>
public readonly IList<ResolveResult> Arguments;

protected InvocationResolveResult(ResolveResult targetResult, IParameterizedMember member, IList<ResolveResult> arguments)
/// <summary>
/// Gets the list of initializer statements that are appplied to the result of this invocation.
/// This is used to represent object and collection initializers.
/// With the initializer statements, the <see cref="InitializedObjectResolveResult"/> is used
/// to refer to the result of this invocation.
/// </summary>
public readonly IList<ResolveResult> InitializerStatements;

public InvocationResolveResult(ResolveResult targetResult, IParameterizedMember member,
IList<ResolveResult> arguments = null,
IList<ResolveResult> initializerStatements = null)
: base(targetResult, member)
{
this.Arguments = arguments ?? EmptyList<ResolveResult>.Instance;
this.InitializerStatements = initializerStatements ?? EmptyList<ResolveResult>.Instance;
}

public new IParameterizedMember Member {
Expand All @@ -58,7 +69,7 @@ public virtual IList<ResolveResult> GetArgumentsForCall()

public override IEnumerable<ResolveResult> GetChildResults()
{
return base.GetChildResults().Concat(this.Arguments);
return base.GetChildResults().Concat(this.Arguments).Concat(this.InitializerStatements);
}
}
}

0 comments on commit d338acc

Please sign in to comment.