Browse Source

Fix icsharpcode/NRefactory#32: ResolveResult for anonymous type creation

newNRvisualizers
Daniel Grunwald 13 years ago
parent
commit
282d3c3423
  1. 40
      ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs
  2. 58
      ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs
  3. 27
      ICSharpCode.NRefactory.ConsistencyCheck/ResolverTest.cs
  4. 21
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/AnonymousTypeTests.cs
  5. 29
      ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs
  6. 29
      ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedMethod.cs
  7. 25
      ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedMethod.cs

40
ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs

@ -2689,25 +2689,31 @@ namespace ICSharpCode.NRefactory.CSharp @@ -2689,25 +2689,31 @@ namespace ICSharpCode.NRefactory.CSharp
public override object Visit (NewAnonymousType newAnonymousType)
{
var result = new AnonymousTypeCreateExpression ();
if (newAnonymousType.Parameters == null)
return result;
foreach (var par in newAnonymousType.Parameters) {
if (par == null)
continue;
var location = LocationsBag.GetLocations (par);
if (location == null) {
if (par.Expr != null)
result.AddChild ((Expression)par.Expr.Accept (this), Roles.Expression);
} else {
var namedExpression = new NamedExpression ();
namedExpression.AddChild (Identifier.Create (par.Name, Convert (par.Location)), Roles.Identifier);
namedExpression.AddChild (new CSharpTokenNode (Convert (location [0])), Roles.Assign);
if (par.Expr != null)
namedExpression.AddChild ((Expression)par.Expr.Accept (this), Roles.Expression);
result.AddChild (namedExpression, Roles.Expression);
var location = LocationsBag.GetLocations (newAnonymousType);
result.AddChild (new CSharpTokenNode (Convert (newAnonymousType.Location)), ObjectCreateExpression.NewKeywordRole);
if (location != null)
result.AddChild (new CSharpTokenNode (Convert (location [0])), Roles.LBrace);
if (newAnonymousType.Parameters != null) {
foreach (var par in newAnonymousType.Parameters) {
if (par == null)
continue;
var parLocation = LocationsBag.GetLocations (par);
if (parLocation == null) {
if (par.Expr != null)
result.AddChild ((Expression)par.Expr.Accept (this), Roles.Expression);
} else {
var namedExpression = new NamedExpression ();
namedExpression.AddChild (Identifier.Create (par.Name, Convert (par.Location)), Roles.Identifier);
namedExpression.AddChild (new CSharpTokenNode (Convert (parLocation [0])), Roles.Assign);
if (par.Expr != null)
namedExpression.AddChild ((Expression)par.Expr.Accept (this), Roles.Expression);
result.AddChild (namedExpression, Roles.Expression);
}
}
}
if (location != null && location.Count > 1)
result.AddChild (new CSharpTokenNode (Convert (location [1])), Roles.RBrace);
return result;
}

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

@ -1144,17 +1144,29 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1144,17 +1144,29 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
return null;
}
class AnonymousTypeMember
{
public readonly Expression Expression;
public readonly ResolveResult Initializer;
public AnonymousTypeMember(Expression expression, ResolveResult initializer)
{
this.Expression = expression;
this.Initializer = initializer;
}
}
ResolveResult IAstVisitor<ResolveResult>.VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression)
{
// 7.6.10.6 Anonymous object creation expressions
List<IUnresolvedProperty> properties = new List<IUnresolvedProperty>();
var initializers = anonymousTypeCreateExpression.Initializers;
foreach (var expr in initializers) {
List<IUnresolvedProperty> unresolvedProperties = new List<IUnresolvedProperty>();
List<AnonymousTypeMember> members = new List<AnonymousTypeMember>();
foreach (var expr in anonymousTypeCreateExpression.Initializers) {
Expression resolveExpr;
var name = GetAnonymousTypePropertyName(expr, out resolveExpr);
if (resolveExpr != null) {
var returnType = Resolve(resolveExpr).Type;
var returnTypeRef = returnType.ToTypeReference();
var initRR = Resolve(resolveExpr);
var returnTypeRef = initRR.Type.ToTypeReference();
var property = new DefaultUnresolvedProperty {
Name = name,
Accessibility = Accessibility.Public,
@ -1165,18 +1177,30 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1165,18 +1177,30 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
ReturnType = returnTypeRef
}
};
properties.Add(property);
}
}
var anonymousType = new AnonymousType(resolver.Compilation, properties);
foreach (var pair in initializers.Zip(anonymousType.GetProperties(), (expr, prop) => new { expr = expr as NamedExpression, prop })) {
if (pair.expr != null) {
StoreCurrentState(pair.expr);
// pair.expr.Expression was already resolved by the first loop
StoreResult(pair.expr, new MemberResolveResult(new ResolveResult(anonymousType), pair.prop));
}
}
return new ResolveResult(anonymousType);
unresolvedProperties.Add(property);
members.Add(new AnonymousTypeMember(expr, initRR));
} else {
Scan(expr);
}
}
var anonymousType = new AnonymousType(resolver.Compilation, unresolvedProperties);
var properties = anonymousType.GetProperties().ToList();
Debug.Assert(properties.Count == members.Count);
List<ResolveResult> assignments = new List<ResolveResult>();
for (int i = 0; i < members.Count; i++) {
ResolveResult lhs = new MemberResolveResult(new InitializedObjectResolveResult(anonymousType), properties[i]);
ResolveResult rhs = members[i].Initializer;
ResolveResult assignment = resolver.ResolveAssignment(AssignmentOperatorType.Assign, lhs, rhs);
var ne = members[i].Expression as NamedExpression;
if (ne != null) {
StoreCurrentState(ne);
// ne.Expression was already resolved by the first loop
StoreResult(ne, lhs);
}
assignments.Add(assignment);
}
var anonymousCtor = DefaultResolvedMethod.GetDummyConstructor(resolver.Compilation, anonymousType);
return new InvocationResolveResult(null, anonymousCtor, initializerStatements: assignments);
}
#endregion

27
ICSharpCode.NRefactory.ConsistencyCheck/ResolverTest.cs

@ -100,30 +100,17 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck @@ -100,30 +100,17 @@ namespace ICSharpCode.NRefactory.ConsistencyCheck
public static void RunTestWithoutParsedFile(CSharpFile file)
{
CSharpAstResolver originalResolver = new CSharpAstResolver(file.Project.Compilation, file.CompilationUnit, file.ParsedFile);
originalResolver.ApplyNavigator(new ValidatingResolveAllNavigator(file.FileName), CancellationToken.None);
CSharpAstResolver resolver = new CSharpAstResolver(file.Project.Compilation, file.CompilationUnit);
var navigator = new ComparingResolveAllNavigator(originalResolver);
var navigator = new ValidatingResolveAllNavigator(file.FileName);
resolver.ApplyNavigator(navigator, CancellationToken.None);
navigator.Validate(resolver, file.CompilationUnit);
}
sealed class ComparingResolveAllNavigator : ValidatingResolveAllNavigator
{
readonly CSharpAstResolver originalResolver;
public ComparingResolveAllNavigator(CSharpAstResolver originalResolver)
: base(originalResolver.ParsedFile.FileName)
{
this.originalResolver = originalResolver;
}
public override void Resolved(AstNode node, ResolveResult result)
{
base.Resolved(node, result);
ResolveResult originalResult = originalResolver.Resolve(node);
if (!RandomizedOrderResolverTest.IsEqualResolveResult(originalResult, result)) {
Console.WriteLine("Compiler error at " + node.GetRegion().FileName + ":" + node.StartLocation + ": Should be " + originalResult + " but was " + result);
CSharpAstResolver originalResolver = new CSharpAstResolver(file.Project.Compilation, file.CompilationUnit, file.ParsedFile);
foreach (var node in file.CompilationUnit.DescendantsAndSelf) {
var originalResult = originalResolver.Resolve(node);
var result = resolver.Resolve(node);
if (!RandomizedOrderResolverTest.IsEqualResolveResult(result, originalResult)) {
Console.WriteLine("Got different without IParsedFile at " + file.FileName + ":" + node.StartLocation);
}
}
}

21
ICSharpCode.NRefactory.Tests/CSharp/Resolver/AnonymousTypeTests.cs

@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
using System;
using System.Linq;
using System.Linq.Expressions;
using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.TypeSystem;
using NUnit.Framework;
@ -60,5 +61,25 @@ class Test { @@ -60,5 +61,25 @@ class Test {
Assert.AreEqual(EntityType.Property, rr.Member.EntityType);
Assert.AreEqual("System.String", rr.Member.ReturnType.FullName);
}
[Test]
public void ZipAnonymousType()
{
string program = programStart + "var q = list1.Zip(list2, (a,b) => $new { a, b }$);" + programEnd;
var rr = Resolve<InvocationResolveResult>(program);
Assert.AreEqual(TypeKind.Anonymous, rr.Type.Kind);
Assert.AreEqual(EntityType.Constructor, rr.Member.EntityType);
Assert.AreEqual(rr.Type, rr.Member.DeclaringType);
Assert.AreEqual(0, rr.Arguments.Count);
Assert.AreEqual(2, rr.InitializerStatements.Count);
var init1 = (OperatorResolveResult)rr.InitializerStatements[0];
Assert.AreEqual(ExpressionType.Assign, init1.OperatorType);
Assert.IsInstanceOf<MemberResolveResult>(init1.Operands[0]);
Assert.IsInstanceOf<LocalResolveResult>(init1.Operands[1]);
ResolveResult target = ((MemberResolveResult)init1.Operands[0]).TargetResult;
Assert.IsInstanceOf<InitializedObjectResolveResult>(target);
Assert.AreEqual(rr.Type, target.Type);
}
}
}

29
ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs

@ -232,37 +232,12 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -232,37 +232,12 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
return EmptyList<IType>.Instance;
}
static readonly IUnresolvedMethod dummyConstructor = CreateDummyConstructor();
static IUnresolvedMethod CreateDummyConstructor()
{
var m = new DefaultUnresolvedMethod {
EntityType = EntityType.Constructor,
Name = ".ctor",
Accessibility = Accessibility.Public,
IsSynthetic = true,
ReturnType = KnownTypeReference.Void
};
m.Freeze();
return m;
}
static IMethod GetDummyConstructor(ICompilation compilation)
{
// Reuse the same IMethod instance for all dummy constructors
// so that two occurrences of 'new T()' refer to the same constructor.
return (IMethod)compilation.CacheManager.GetOrAddShared(
dummyConstructor, _ => dummyConstructor.CreateResolved(compilation.TypeResolveContext));
}
public IEnumerable<IMethod> GetConstructors(Predicate<IUnresolvedMethod> filter = null, GetMemberOptions options = GetMemberOptions.IgnoreInheritedMembers)
{
if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers) {
if (this.HasDefaultConstructorConstraint || this.HasValueTypeConstraint) {
if (filter == null || filter(dummyConstructor)) {
var resolvedCtor = GetDummyConstructor(compilation);
IMethod m = new SpecializedMethod(resolvedCtor, TypeParameterSubstitution.Identity) { DeclaringType = this };
return new [] { m };
if (filter == null || filter(DefaultUnresolvedMethod.DummyConstructor)) {
return new [] { DefaultResolvedMethod.GetDummyConstructor(compilation, this) };
}
}
return EmptyList<IMethod>.Instance;

29
ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedMethod.cs

@ -202,5 +202,34 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -202,5 +202,34 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
this.Parameters.Select(p => p.Type.ToTypeReference()).ToList());
}
}
/// <summary>
/// Gets a dummy constructor for the specified compilation.
/// </summary>
/// <returns>
/// A public instance constructor with IsSynthetic=true and no declaring type.
/// </returns>
/// <seealso cref="DefaultUnresolvedMethod.DummyConstructor"/>
public static IMethod GetDummyConstructor(ICompilation compilation)
{
var dummyConstructor = DefaultUnresolvedMethod.DummyConstructor;
// Reuse the same IMethod instance for all dummy constructors
// so that two occurrences of 'new T()' refer to the same constructor.
return (IMethod)compilation.CacheManager.GetOrAddShared(
dummyConstructor, _ => dummyConstructor.CreateResolved(compilation.TypeResolveContext));
}
/// <summary>
/// Gets a dummy constructor for the specified type.
/// </summary>
/// <returns>
/// A public instance constructor with IsSynthetic=true and the specified declaring type.
/// </returns>
/// <seealso cref="DefaultUnresolvedMethod.DummyConstructor"/>
public static IMethod GetDummyConstructor(ICompilation compilation, IType declaringType)
{
var resolvedCtor = GetDummyConstructor(compilation);
return new SpecializedMethod(resolvedCtor, TypeParameterSubstitution.Identity) { DeclaringType = declaringType };
}
}
}

25
ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedMethod.cs

@ -161,5 +161,30 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -161,5 +161,30 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
ReturnType = KnownTypeReference.Void
};
}
static readonly IUnresolvedMethod dummyConstructor = CreateDummyConstructor();
/// <summary>
/// Returns a dummy constructor instance:
/// </summary>
/// <returns>
/// A public instance constructor with IsSynthetic=true and no declaring type.
/// </returns>
public static IUnresolvedMethod DummyConstructor {
get { return dummyConstructor; }
}
static IUnresolvedMethod CreateDummyConstructor()
{
var m = new DefaultUnresolvedMethod {
EntityType = EntityType.Constructor,
Name = ".ctor",
Accessibility = Accessibility.Public,
IsSynthetic = true,
ReturnType = KnownTypeReference.Void
};
m.Freeze();
return m;
}
}
}

Loading…
Cancel
Save