From 0b263b0b74d0ebb85e0db8c73233b53eaae6a98f Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 25 Nov 2011 19:24:44 +0100 Subject: [PATCH] TypeSystemConvertVisitor: implemented ConvertInterfaceImplementation for methods and properties. Implemented 'goto case' support in control flow analysis. --- .../Analysis/ControlFlow.cs | 44 ++++++++++-- .../Resolver/CSharpAstResolver.cs | 4 ++ .../TypeSystem/TypeSystemConvertVisitor.cs | 16 +++-- .../Analysis/DefiniteAssignmentTests.cs | 68 +++++++++++++++++++ .../TypeSystem/CecilLoader.cs | 10 ++- .../DefaultResolvedTypeDefinition.cs | 13 +++- .../Implementation/DummyTypeParameter.cs | 2 +- 7 files changed, 145 insertions(+), 12 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Analysis/ControlFlow.cs b/ICSharpCode.NRefactory.CSharp/Analysis/ControlFlow.cs index 8d58a944c7..92caf81a8f 100644 --- a/ICSharpCode.NRefactory.CSharp/Analysis/ControlFlow.cs +++ b/ICSharpCode.NRefactory.CSharp/Analysis/ControlFlow.cs @@ -21,9 +21,9 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; + using ICSharpCode.NRefactory.CSharp.Resolver; using ICSharpCode.NRefactory.Semantics; -using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem.Implementation; namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -429,7 +429,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis foreach (CaseLabel label in section.CaseLabels) { if (label.Expression.IsNull) { defaultSection = section; - } else if (constant != null) { + } else if (constant != null && constant.IsCompileTimeConstant) { ResolveResult labelConstant = builder.EvaluateConstant(label.Expression); if (builder.AreEqualConstants(constant, labelConstant)) sectionMatchedByConstant = section; @@ -440,17 +440,22 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis sectionMatchedByConstant = defaultSection; int gotoCaseOrDefaultInOuterScope = gotoCaseOrDefault.Count; + List sectionStartNodes = new List(); ControlFlowNode end = builder.CreateEndNode(switchStatement, addToNodeList: false); breakTargets.Push(end); foreach (SwitchSection section in switchStatement.SwitchSections) { - if (constant == null || section == sectionMatchedByConstant) { + int sectionStartNodeID = builder.nodes.Count; + if (constant == null || !constant.IsCompileTimeConstant || section == sectionMatchedByConstant) { HandleStatementList(section.Statements, data); } else { // This section is unreachable: pass null to HandleStatementList. HandleStatementList(section.Statements, null); } // Don't bother connecting the ends of the sections: the 'break' statement takes care of that. + + // Store the section start node for 'goto case' statements. + sectionStartNodes.Add(sectionStartNodeID < builder.nodes.Count ? builder.nodes[sectionStartNodeID] : null); } breakTargets.Pop(); if (defaultSection == null && sectionMatchedByConstant == null) { @@ -459,7 +464,38 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis if (gotoCaseOrDefault.Count > gotoCaseOrDefaultInOuterScope) { // Resolve 'goto case' statements: - throw new NotImplementedException(); + for (int i = gotoCaseOrDefaultInOuterScope; i < gotoCaseOrDefault.Count; i++) { + ControlFlowNode gotoCaseNode = gotoCaseOrDefault[i]; + GotoCaseStatement gotoCaseStatement = gotoCaseNode.NextStatement as GotoCaseStatement; + ResolveResult gotoCaseConstant = null; + if (gotoCaseStatement != null) { + gotoCaseConstant = builder.EvaluateConstant(gotoCaseStatement.LabelExpression); + } + int targetSectionIndex = -1; + int currentSectionIndex = 0; + foreach (SwitchSection section in switchStatement.SwitchSections) { + foreach (CaseLabel label in section.CaseLabels) { + if (gotoCaseStatement != null) { + // goto case + if (!label.Expression.IsNull) { + ResolveResult labelConstant = builder.EvaluateConstant(label.Expression); + if (builder.AreEqualConstants(gotoCaseConstant, labelConstant)) + targetSectionIndex = currentSectionIndex; + } + } else { + // goto default + if (label.Expression.IsNull) + targetSectionIndex = currentSectionIndex; + } + } + currentSectionIndex++; + } + if (targetSectionIndex >= 0 && sectionStartNodes[targetSectionIndex] != null) + Connect(gotoCaseNode, sectionStartNodes[targetSectionIndex], ControlFlowEdgeType.Jump); + else + Connect(gotoCaseNode, end, ControlFlowEdgeType.Jump); + } + gotoCaseOrDefault.RemoveRange(gotoCaseOrDefaultInOuterScope, gotoCaseOrDefault.Count - gotoCaseOrDefaultInOuterScope); } builder.nodes.Add(end); diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpAstResolver.cs b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpAstResolver.cs index d7b6f3d2f6..ee977a47fb 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpAstResolver.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpAstResolver.cs @@ -89,6 +89,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// public void ApplyNavigator(IResolveVisitorNavigator navigator, CancellationToken cancellationToken = default(CancellationToken)) { + if (navigator == null) + throw new ArgumentNullException("navigator"); if (resolveVisitor != null) throw new InvalidOperationException("Applying a navigator is only valid as the first operation on the CSharpAstResolver."); resolveVisitor = new ResolveVisitor(initialResolverState, parsedFile, navigator); @@ -101,6 +103,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// public ResolveResult Resolve(AstNode node, CancellationToken cancellationToken = default(CancellationToken)) { + if (node == null || node.IsNull) + return ErrorResolveResult.UnknownError; if (resolveVisitor == null) { resolveVisitor = new ResolveVisitor(initialResolverState, parsedFile); resolveVisitor.Scan(rootNode); diff --git a/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeSystemConvertVisitor.cs b/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeSystemConvertVisitor.cs index 16aff4b0a0..c6a15f48cf 100644 --- a/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeSystemConvertVisitor.cs +++ b/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeSystemConvertVisitor.cs @@ -494,11 +494,19 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem IMemberReference ConvertInterfaceImplementation(AstType interfaceType, AbstractUnresolvedMember unresolvedMember) { ITypeReference interfaceTypeReference = ConvertType(interfaceType); - if (unresolvedMember.EntityType == EntityType.Method || unresolvedMember.EntityType == EntityType.Indexer) { - throw new NotImplementedException(); - } else { - return new DefaultMemberReference(unresolvedMember.EntityType, ConvertType(interfaceType), unresolvedMember.Name); + int typeParameterCount = 0; + IList parameterTypes = null; + if (unresolvedMember.EntityType == EntityType.Method) { + typeParameterCount = ((IUnresolvedMethod)unresolvedMember).TypeParameters.Count; + } + IUnresolvedParameterizedMember parameterizedMember = unresolvedMember as IUnresolvedParameterizedMember; + if (parameterizedMember != null) { + parameterTypes = new ITypeReference[parameterizedMember.Parameters.Count]; + for (int i = 0; i < parameterTypes.Count; i++) { + parameterTypes[i] = parameterizedMember.Parameters[i].Type; + } } + return new DefaultMemberReference(unresolvedMember.EntityType, ConvertType(interfaceType), unresolvedMember.Name, typeParameterCount, parameterTypes); } #endregion diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs index 9790990709..8d077fdb13 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Analysis/DefiniteAssignmentTests.cs @@ -19,6 +19,7 @@ using System; using System.Linq; using System.Threading; + using ICSharpCode.NRefactory.CSharp.Resolver; using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem.Implementation; @@ -221,5 +222,72 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(loop.Iterators.Single())); Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusAfter(loop)); } + + [Test] + public void SwitchWithGotoDefault() + { + SwitchStatement @switch = new SwitchStatement { + SwitchSections = { + new SwitchSection { // case 0: + CaseLabels = { new CaseLabel(new PrimitiveExpression(0)) }, + Statements = { new GotoDefaultStatement() } + }, + new SwitchSection { // default: + CaseLabels = { new CaseLabel() }, + Statements = { + new ExpressionStatement(new AssignmentExpression(new IdentifierExpression("a"), new PrimitiveExpression(1))), + new BreakStatement() + } + } + }}; + + SwitchSection case0 = @switch.SwitchSections.ElementAt(0); + SwitchSection defaultSection = @switch.SwitchSections.ElementAt(1); + + DefiniteAssignmentAnalysis da = CreateDefiniteAssignmentAnalysis(@switch); + da.Analyze("a"); + Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(@switch)); + Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(case0.Statements.First())); + Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(defaultSection.Statements.First())); + Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusBefore(defaultSection.Statements.Last())); + Assert.AreEqual(DefiniteAssignmentStatus.CodeUnreachable, da.GetStatusAfter(defaultSection.Statements.Last())); + Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(@switch)); + } + + [Test] + public void SwitchWithGotoCase() + { + SwitchStatement @switch = new SwitchStatement { + Expression = new PrimitiveExpression(1), + SwitchSections = { + new SwitchSection { // case 0: + CaseLabels = { new CaseLabel(new PrimitiveExpression(0)) }, + Statements = { new BreakStatement() } + }, + new SwitchSection { // case 1: + CaseLabels = { new CaseLabel(new PrimitiveExpression(1)) }, + Statements = { + new ExpressionStatement(new AssignmentExpression(new IdentifierExpression("a"), new PrimitiveExpression(0))), + new GotoCaseStatement { LabelExpression = new PrimitiveExpression(2) } + } + }, + new SwitchSection { // case 2: + CaseLabels = { new CaseLabel(new PrimitiveExpression(2)) }, + Statements = { new BreakStatement() } + } + }}; + + SwitchSection case0 = @switch.SwitchSections.ElementAt(0); + SwitchSection case1 = @switch.SwitchSections.ElementAt(1); + SwitchSection case2 = @switch.SwitchSections.ElementAt(2); + + DefiniteAssignmentAnalysis da = CreateDefiniteAssignmentAnalysis(@switch); + da.Analyze("a"); + Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(@switch)); + Assert.AreEqual(DefiniteAssignmentStatus.CodeUnreachable, da.GetStatusBefore(case0.Statements.First())); + Assert.AreEqual(DefiniteAssignmentStatus.PotentiallyAssigned, da.GetStatusBefore(case1.Statements.First())); + Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusBefore(case2.Statements.First())); + Assert.AreEqual(DefiniteAssignmentStatus.DefinitelyAssigned, da.GetStatusAfter(@switch)); + } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs index f65ecd7724..5549bf7a1f 100644 --- a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs +++ b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs @@ -1236,7 +1236,6 @@ namespace ICSharpCode.NRefactory.TypeSystem default: throw new NotSupportedException(); } - throw new NotImplementedException(); } IType ReadType() @@ -1246,8 +1245,15 @@ namespace ICSharpCode.NRefactory.TypeSystem IType typeInCurrentAssembly = typeReference.Resolve(new SimpleTypeResolveContext(currentResolvedAssembly)); if (typeInCurrentAssembly.Kind != TypeKind.Unknown) return typeInCurrentAssembly; + // look for the type in mscorlib - throw new NotImplementedException(); + ITypeDefinition systemObject = currentResolvedAssembly.Compilation.FindType(KnownTypeCode.Object).GetDefinition(); + if (systemObject != null) { + return typeReference.Resolve(new SimpleTypeResolveContext(systemObject.ParentAssembly)); + } else { + // couldn't find corlib - return the unknown IType for the current assembly + return typeInCurrentAssembly; + } } } #endregion diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs index 344a56e001..91dcceef34 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs @@ -435,7 +435,18 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation public ITypeReference ToTypeReference() { - throw new NotImplementedException(); + ITypeDefinition declTypeDef = this.DeclaringTypeDefinition; + if (declTypeDef != null) { + return new NestedTypeReference(declTypeDef.ToTypeReference(), this.Name, this.TypeParameterCount - declTypeDef.TypeParameterCount); + } else { + IAssembly asm = this.ParentAssembly; + IAssemblyReference asmRef; + if (asm != null) + asmRef = new DefaultAssemblyReference(asm.AssemblyName); + else + asmRef = DefaultAssemblyReference.CurrentAssembly; + return new GetClassTypeReference(asmRef, this.Namespace, this.Name, this.TypeParameterCount); + } } public IEnumerable GetNestedTypes(Predicate filter = null, GetMemberOptions options = GetMemberOptions.None) diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DummyTypeParameter.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DummyTypeParameter.cs index 97b9c5a0fa..65d43c8234 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DummyTypeParameter.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DummyTypeParameter.cs @@ -84,7 +84,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation public override ITypeReference ToTypeReference() { - throw new NotSupportedException(); + return new TypeParameterReference(ownerType, index); } public int Index {