diff --git a/ICSharpCode.NRefactory.CSharp/Ast/CompilationUnit.cs b/ICSharpCode.NRefactory.CSharp/Ast/CompilationUnit.cs index 55ab8a30ec..04dec11e75 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/CompilationUnit.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/CompilationUnit.cs @@ -58,6 +58,10 @@ namespace ICSharpCode.NRefactory.CSharp } } + public AstNodeCollection Members { + get { return GetChildrenByRole(MemberRole); } + } + List errors = new List (); public List Errors { diff --git a/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PrimitiveExpression.cs b/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PrimitiveExpression.cs index 97bd084365..de08ee2623 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PrimitiveExpression.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PrimitiveExpression.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +using System; + namespace ICSharpCode.NRefactory.CSharp { /// @@ -60,6 +62,8 @@ namespace ICSharpCode.NRefactory.CSharp public string LiteralValue { get { return literalValue; } set { + if (value == null) + throw new ArgumentNullException(); ThrowIfFrozen(); literalValue = value; } @@ -68,6 +72,7 @@ namespace ICSharpCode.NRefactory.CSharp public PrimitiveExpression (object value) { this.Value = value; + this.literalValue = ""; } public PrimitiveExpression (object value, string literalValue) diff --git a/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeDeclaration.cs b/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeDeclaration.cs index c6c25f5e25..d4ac6eaf65 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeDeclaration.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeDeclaration.cs @@ -41,7 +41,7 @@ namespace ICSharpCode.NRefactory.CSharp /// /// class Name<TypeParameters> : BaseTypes where Constraints; /// - public abstract class TypeDeclaration : EntityDeclaration + public class TypeDeclaration : EntityDeclaration { public override NodeType NodeType { get { return NodeType.TypeDeclaration; } @@ -51,13 +51,31 @@ namespace ICSharpCode.NRefactory.CSharp get { return EntityType.TypeDefinition; } } + ClassType classType; - public abstract CSharpTokenNode TypeKeyword { - get; + public CSharpTokenNode TypeKeyword { + get { + switch (classType) { + case ClassType.Class: + return GetChildByRole(Roles.ClassKeyword); + case ClassType.Struct: + return GetChildByRole(Roles.StructKeyword); + case ClassType.Interface: + return GetChildByRole(Roles.InterfaceKeyword); + case ClassType.Enum: + return GetChildByRole(Roles.EnumKeyword); + default: + return CSharpTokenNode.Null; + } + } } - - public abstract ClassType ClassType { - get; + + public ClassType ClassType { + get { return classType; } + set { + ThrowIfFrozen(); + classType = value; + } } public AstNodeCollection TypeParameters { @@ -88,7 +106,7 @@ namespace ICSharpCode.NRefactory.CSharp { visitor.VisitTypeDeclaration (this); } - + public override T AcceptVisitor (IAstVisitor visitor) { return visitor.VisitTypeDeclaration (this); @@ -107,87 +125,5 @@ namespace ICSharpCode.NRefactory.CSharp && this.BaseTypes.DoMatch(o.BaseTypes, match) && this.Constraints.DoMatch(o.Constraints, match) && this.Members.DoMatch(o.Members, match); } - - protected TypeDeclaration () - { - } - - public static TypeDeclaration Create(ClassType type) - { - switch (type) { - case ICSharpCode.NRefactory.CSharp.ClassType.Class: - return new Class (); - case ICSharpCode.NRefactory.CSharp.ClassType.Struct: - return new Struct (); - case ICSharpCode.NRefactory.CSharp.ClassType.Interface: - return new Interface (); - case ICSharpCode.NRefactory.CSharp.ClassType.Enum: - return new Enum (); - default: - throw new System.ArgumentOutOfRangeException(); - } - } - - #region Concrete Types - public class Class : TypeDeclaration - { - public override ClassType ClassType { - get { - return ClassType.Class; - } - } - - public override CSharpTokenNode TypeKeyword { - get { - return GetChildByRole(Roles.ClassKeyword); - } - } - } - - public class Struct : TypeDeclaration - { - public override ClassType ClassType { - get { - return ClassType.Struct; - } - } - - public override CSharpTokenNode TypeKeyword { - get { - return GetChildByRole(Roles.StructKeyword); - } - } - } - - public class Interface : TypeDeclaration - { - public override ClassType ClassType { - get { - return ClassType.Interface; - } - } - - public override CSharpTokenNode TypeKeyword { - get { - return GetChildByRole(Roles.InterfaceKeyword); - } - } - } - - public class Enum : TypeDeclaration - { - public override ClassType ClassType { - get { - return ClassType.Enum; - } - } - - public override CSharpTokenNode TypeKeyword { - get { - return GetChildByRole(Roles.EnumKeyword); - } - } - } - #endregion } } diff --git a/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs b/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs index d4aea35aed..a5e1f0f100 100644 --- a/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs +++ b/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs @@ -1,4 +1,4 @@ -// +// // CSharpParser.cs // // Author: @@ -483,7 +483,8 @@ namespace ICSharpCode.NRefactory.CSharp public override void Visit(Class c) { - var newType = new TypeDeclaration.Class (); + var newType = new TypeDeclaration (); + newType.ClassType = ClassType.Class; AddAttributeSection(newType, c); var location = LocationsBag.GetMemberLocation(c); @@ -533,7 +534,8 @@ namespace ICSharpCode.NRefactory.CSharp public override void Visit(Struct s) { - var newType = new TypeDeclaration.Struct(); + var newType = new TypeDeclaration(); + newType.ClassType = ClassType.Struct; AddAttributeSection(newType, s); var location = LocationsBag.GetMemberLocation(s); AddModifiers(newType, location); @@ -577,7 +579,8 @@ namespace ICSharpCode.NRefactory.CSharp public override void Visit(Interface i) { - var newType = new TypeDeclaration.Interface (); + var newType = new TypeDeclaration(); + newType.ClassType = ClassType.Interface; AddAttributeSection(newType, i); var location = LocationsBag.GetMemberLocation(i); AddModifiers(newType, location); @@ -667,7 +670,8 @@ namespace ICSharpCode.NRefactory.CSharp public override void Visit(Mono.CSharp.Enum e) { - var newType = new TypeDeclaration.Enum (); + var newType = new TypeDeclaration(); + newType.ClassType = ClassType.Enum; AddAttributeSection(newType, e); var location = LocationsBag.GetMemberLocation(e); diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/TypeSystemAstBuilder.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/TypeSystemAstBuilder.cs index a3682bdc8c..fa52949a20 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/TypeSystemAstBuilder.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/TypeSystemAstBuilder.cs @@ -476,7 +476,8 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring break; } - var decl = TypeDeclaration.Create(classType); + var decl = new TypeDeclaration(); + decl.ClassType = classType; decl.Modifiers = modifiers; decl.Name = typeDefinition.Name; diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs b/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs index 1a30d2b8bd..113c7cb3d4 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs @@ -248,7 +248,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver #if DEBUG CSharpResolver oldResolver; if (resolverBeforeDict.TryGetValue(node, out oldResolver)) { - Debug.Assert(oldResolver.LocalVariables.Count() == resolver.LocalVariables.Count()); + Debug.Assert(oldResolver.LocalVariables.SequenceEqual(resolver.LocalVariables)); } #endif @@ -1891,6 +1891,26 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver #endregion #region Implicitly typed + // Implicitly-typed lambdas are really complex, as the lambda depends on the target type (the delegate to which + // the lambda is converted), but figuring out the target type might involve overload resolution (for method + // calls in which the lambda is used as argument), which requires knowledge about the lamdba. + // + // The implementation in NRefactory works like this: + // 1. The lambda resolves to a ImplicitlyTypedLambda (derived from LambdaResolveResult). + // The lambda body is not resolved yet (one of the few places where ResolveVisitor + // deviates from the usual depth-first AST traversal). + // 2. The parent statement is resolved as usual. This might require analyzing the lambda in detail (for example + // as part of overload resolution). Such analysis happens using LambdaResolveResult.IsValid, where the caller + // (i.e. the overload resolution algorithm) supplies the parameter types to the lambda body. For every IsValid() + // call, a nested LambdaTypeHypothesis is constructed for analyzing the lambda using the supplied type assignment. + // Multiple IsValid() calls may use several LambdaTypeHypothesis instances, one for each set of parameter types. + // 3. When the resolver reports the conversions that occurred as part of the parent statement (as with any + // conversions), the results from the LambdaTypeHypothesis corresponding to the actually chosen + // conversion are merged into the main resolver. + // 4. LambdaResolveResult.Body is set to the main resolve result from the chosen nested resolver. I think this + // is the only place where NRefactory is mutating a ResolveResult (normally all resolve results are immutable). + // As this step is guaranteed to occur before the resolver returns the LamdbaResolveResult to user code, the + // mutation shouldn't cause any problems. sealed class ImplicitlyTypedLambda : LambdaBase { readonly LambdaExpression lambda; @@ -1899,7 +1919,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver readonly CSharpResolver storedContext; readonly CSharpParsedFile parsedFile; readonly List hypotheses = new List(); - readonly List parameters = new List(); + internal IList parameters = new List(); internal LambdaTypeHypothesis winningHypothesis; internal ResolveResult bodyResult; @@ -1953,7 +1973,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver : this(parentVisitor) { this.selectClause = selectClause; - this.parameters.AddRange(parameters); + foreach (IParameter p in parameters) + this.parameters.Add(p); RegisterUndecidedLambda(); } @@ -2078,7 +2099,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver sealed class LambdaTypeHypothesis : IResolveVisitorNavigator { readonly ImplicitlyTypedLambda lambda; - internal readonly IParameter[] lambdaParameters; + readonly IParameter[] lambdaParameters; internal readonly IType[] parameterTypes; readonly ResolveVisitor visitor; @@ -2172,6 +2193,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver throw new InvalidOperationException("Trying to merge conflicting hypotheses"); lambda.winningHypothesis = this; + lambda.parameters = lambdaParameters; // replace untyped parameters with typed parameters if (lambda.BodyExpression is Expression && returnValues.Count == 1) { lambda.bodyResult = returnValues[0]; } @@ -3365,9 +3387,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } implicitlyTypedLambda.EnforceMerge(this); - if (implicitlyTypedLambda.winningHypothesis.parameterTypes.Length == 2) { + if (implicitlyTypedLambda.Parameters.Count == 2) { StoreCurrentState(queryJoinClause.IntoIdentifierToken); - groupVariable = implicitlyTypedLambda.winningHypothesis.lambdaParameters[1]; + groupVariable = implicitlyTypedLambda.Parameters[1]; } else { groupVariable = null; } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CSharpOutputVisitorTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CSharpOutputVisitorTests.cs index 24c44bc3e8..d86a049adf 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CSharpOutputVisitorTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CSharpOutputVisitorTests.cs @@ -53,7 +53,8 @@ namespace ICSharpCode.NRefactory.CSharp [Test] public void EnumDeclarationWithInitializers () { - TypeDeclaration type = new TypeDeclaration.Enum { + TypeDeclaration type = new TypeDeclaration { + ClassType = ClassType.Enum, Name = "DisplayFlags", Members = { new EnumMemberDeclaration { diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Parser/GeneralScope/AttributeSectionTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Parser/GeneralScope/AttributeSectionTests.cs index 8dd47d6b91..cdf8e1cd09 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Parser/GeneralScope/AttributeSectionTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Parser/GeneralScope/AttributeSectionTests.cs @@ -97,7 +97,8 @@ public class Form1 { { ParseUtilCSharp.AssertGlobal( @"[A, B] class Test {}", - new TypeDeclaration.Class { + new TypeDeclaration { + ClassType = ClassType.Class, Name = "Test", Attributes = { new AttributeSection { @@ -113,7 +114,8 @@ public class Form1 { { ParseUtilCSharp.AssertGlobal( "class Test<[A,B]C> {}", - new TypeDeclaration.Class { + new TypeDeclaration { + ClassType = ClassType.Class, Name = "Test", TypeParameters = { new TypeParameterDeclaration { @@ -172,7 +174,8 @@ public class Form1 { { ParseUtilCSharp.AssertTypeMember ( @"[A(0, a:1, b=2)] class Test {}", - new TypeDeclaration.Class { + new TypeDeclaration { + ClassType = ClassType.Class, Name = "Test", Attributes = { new AttributeSection { diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Parser/GeneralScope/TypeDeclarationTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Parser/GeneralScope/TypeDeclarationTests.cs index 7c40d5f78c..1bae5fb00d 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Parser/GeneralScope/TypeDeclarationTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Parser/GeneralScope/TypeDeclarationTests.cs @@ -89,7 +89,8 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.GeneralScope { ParseUtilCSharp.AssertGlobal( "public class G {}", - new TypeDeclaration.Class { + new TypeDeclaration { + ClassType = ClassType.Class, Modifiers = Modifiers.Public, Name = "G", TypeParameters = { new TypeParameterDeclaration { Name = "T" } } @@ -101,7 +102,8 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.GeneralScope { ParseUtilCSharp.AssertGlobal( @"public class Test where T : IMyInterface { }", - new TypeDeclaration.Class { + new TypeDeclaration { + ClassType = ClassType.Class, Modifiers = Modifiers.Public, Name = "Test", TypeParameters = { new TypeParameterDeclaration { Name = "T" } }, @@ -118,7 +120,8 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.GeneralScope { ParseUtilCSharp.AssertGlobal( "public interface Generic : System.IComparable where S : G, new() where T : MyNamespace.IMyInterface {}", - new TypeDeclaration.Interface { + new TypeDeclaration { + ClassType = ClassType.Interface, Modifiers = Modifiers.Public, Name = "Generic", TypeParameters = { @@ -164,7 +167,8 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.GeneralScope public abstract class MyClass : MyBase, Interface1, My.Test.Interface2 { }", - new TypeDeclaration.Class { + new TypeDeclaration { + ClassType = ClassType.Class, Attributes = { new AttributeSection { Attributes = { @@ -219,7 +223,8 @@ public abstract class MyClass : MyBase, Interface1, My.Test.Interface2 { ParseUtilCSharp.AssertGlobal( "partial class partial<[partial: where] where> where where : partial { }", - new TypeDeclaration.Class { + new TypeDeclaration { + ClassType = ClassType.Class, Modifiers = Modifiers.Partial, Name = "partial", TypeParameters = { @@ -304,7 +309,8 @@ public abstract class MyClass : MyBase, Interface1, My.Test.Interface2 { ParseUtilCSharp.AssertGlobal ( "enum DisplayFlags { D = 4\r\r\n}", - new TypeDeclaration.Enum { + new TypeDeclaration { + ClassType = ClassType.Enum, Name = "DisplayFlags", Members = { new EnumMemberDeclaration { diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/MethodDeclarationTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/MethodDeclarationTests.cs index b126ac1081..0bd880be62 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/MethodDeclarationTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/MethodDeclarationTests.cs @@ -165,7 +165,8 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.TypeMembers T MyMethod(T a) where T : ISomeInterface; } ", - new TypeDeclaration.Interface { + new TypeDeclaration { + ClassType = ClassType.Interface, Name = "MyInterface", Members = { new MethodDeclaration { @@ -190,7 +191,8 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.TypeMembers void MyMethod(T a) where T : ISomeInterface; } ", - new TypeDeclaration.Interface { + new TypeDeclaration { + ClassType = ClassType.Interface, Name = "MyInterface", Members = { new MethodDeclaration { @@ -215,7 +217,8 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.TypeMembers new void Dispose(); } ", - new TypeDeclaration.Interface { + new TypeDeclaration { + ClassType = ClassType.Interface, Name = "MyInterface", BaseTypes = { new SimpleType("IDisposable") }, Members = { diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LambdaTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LambdaTests.cs index 5c15c2f566..734aab3bf2 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LambdaTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LambdaTests.cs @@ -17,7 +17,10 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.IO; using System.Linq; + +using ICSharpCode.NRefactory.CSharp.TypeSystem; using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem.Implementation; @@ -560,5 +563,29 @@ class Test { Assert.IsFalse(rr.IsError); Assert.AreEqual("System.Threading.Tasks.Task`1[[System.Int32]]", rr.Type.ReflectionName); } + + [Test] + public void LambdaParameterIdentity() + { + string code = @"using System; +class TestClass { + void F() { + Func f = $i => i + 1$; + } +}"; + + var prep = PrepareResolver(code); + var lambda = (LambdaExpression)prep.Item2; + var identifierInLambdaBody = ((BinaryOperatorExpression)lambda.Body).Left; + var resolver = prep.Item1; + + var resolvedParameter = ((LocalResolveResult)resolver.Resolve(lambda.Parameters.Single())).Variable; + var parameterInResolveResult = ((LambdaResolveResult)resolver.Resolve(lambda)).Parameters[0]; + var referencedParameter = ((LocalResolveResult)resolver.Resolve(identifierInLambdaBody)).Variable; + + Assert.AreEqual("System.Int32" ,resolvedParameter.Type.ReflectionName); + Assert.AreSame(resolvedParameter, parameterInResolveResult); + Assert.AreSame(resolvedParameter, referencedParameter); + } } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs index d89ca2683d..6a6b4f60d2 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs @@ -143,7 +143,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } - IEnumerable FindDollarSigns(string code) + protected IEnumerable FindDollarSigns(string code) { int line = 1; int col = 1; @@ -172,12 +172,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver project = project.UpdateProjectContent(null, parsedFile); compilation = project.CreateCompilation(); - FindNodeVisitor fnv = new FindNodeVisitor(dollars[0], dollars[1]); - cu.AcceptVisitor(fnv); - Assert.IsNotNull(fnv.ResultNode, "Did not find DOM node at the specified location"); - CSharpAstResolver resolver = new CSharpAstResolver(compilation, cu, parsedFile); - return Tuple.Create(resolver, fnv.ResultNode); + return Tuple.Create(resolver, FindNode(cu, dollars[0], dollars[1])); } protected ResolveResult Resolve(string code) @@ -216,6 +212,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return (T)rr; } + protected AstNode FindNode(CompilationUnit cu, TextLocation start, TextLocation end) + { + FindNodeVisitor fnv = new FindNodeVisitor(start, end); + cu.AcceptVisitor(fnv); + Assert.IsNotNull(fnv.ResultNode, "Did not find DOM node at the specified location"); + return fnv.ResultNode; + } + sealed class FindNodeVisitor : DepthFirstAstVisitor { readonly TextLocation start;