diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs index fcb8af2bba..f79afa9e86 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs @@ -2256,18 +2256,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion return; } string typeString = GetShortType(resolvedType, state); - if (typeString.Contains(".")) { - completionList.AddType(resolvedType, typeString); - } - foreach (var field in resolvedType.GetFields ()) { - if (field.IsConst || field.IsStatic) { - completionList.Result.Add(factory.CreateEntityCompletionData( - field, - typeString + "." + field.Name - ) - ); - } - } + completionList.AddEnumMembers (resolvedType, state, typeString); DefaultCompletionString = typeString; } diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CompletionDataWrapper.cs b/ICSharpCode.NRefactory.CSharp/Completion/CompletionDataWrapper.cs index 0782da3947..d244b97771 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/CompletionDataWrapper.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/CompletionDataWrapper.cs @@ -28,6 +28,7 @@ using System.Collections.Generic; using ICSharpCode.NRefactory.Completion; using ICSharpCode.NRefactory.TypeSystem; using System.Linq; +using ICSharpCode.NRefactory.CSharp.Resolver; namespace ICSharpCode.NRefactory.CSharp.Completion { @@ -239,6 +240,25 @@ namespace ICSharpCode.NRefactory.CSharp.Completion return 1; } } + HashSet addedEnums = new HashSet (); + public void AddEnumMembers (IType resolvedType, CSharpResolver state, string typeString) + { + if (addedEnums.Contains (resolvedType)) + return; + addedEnums.Add (resolvedType); + if (typeString.Contains(".")) { + AddType(resolvedType, typeString); + } + foreach (var field in resolvedType.GetFields ()) { + if (field.IsPublic && (field.IsConst || field.IsStatic)) { + Result.Add(Factory.CreateEntityCompletionData( + field, + typeString + "." + field.Name + ) + ); + } + } + } } } diff --git a/ICSharpCode.NRefactory.CSharp/Formatter/AstFormattingVisitor.cs b/ICSharpCode.NRefactory.CSharp/Formatter/AstFormattingVisitor.cs index 5d91a97ca5..ac58953794 100644 --- a/ICSharpCode.NRefactory.CSharp/Formatter/AstFormattingVisitor.cs +++ b/ICSharpCode.NRefactory.CSharp/Formatter/AstFormattingVisitor.cs @@ -97,6 +97,11 @@ namespace ICSharpCode.NRefactory.CSharp get; set; } + + public DomRegion FormattingRegion { + get; + set; + } public AstFormattingVisitor(CSharpFormattingOptions policy, IDocument document, TextEditorOptions options = null) { @@ -111,6 +116,22 @@ namespace ICSharpCode.NRefactory.CSharp this.options = options ?? TextEditorOptions.Default; curIndent = new Indent(this.options); } + + protected virtual void VisitChildren (AstNode node) + { + if (!FormattingRegion.IsEmpty) { + if (node.EndLocation < FormattingRegion.Begin || node.StartLocation > FormattingRegion.End) + return; + } + + AstNode next; + for (var child = node.FirstChild; child != null; child = next) { + // Store next to allow the loop to continue + // if the visitor removes/replaces child. + next = child.NextSibling; + child.AcceptVisitor (this); + } + } /// /// Applies the changes to the input document. diff --git a/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpOutputVisitor.cs index 40876d2bfb..23932e802c 100644 --- a/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -1007,6 +1007,14 @@ namespace ICSharpCode.NRefactory.CSharp EndNode(primitiveExpression); } + public static string PrintPrimitiveValue(object val) + { + StringWriter writer = new StringWriter(); + CSharpOutputVisitor visitor = new CSharpOutputVisitor(writer, new CSharpFormattingOptions()); + visitor.WritePrimitiveValue(val); + return writer.ToString(); + } + void WritePrimitiveValue(object val) { if (val == null) { diff --git a/ICSharpCode.NRefactory.CSharp/OutputVisitor/CodeDomConvertVisitor.cs b/ICSharpCode.NRefactory.CSharp/OutputVisitor/CodeDomConvertVisitor.cs index 53584eef08..b8e94dcfb5 100644 --- a/ICSharpCode.NRefactory.CSharp/OutputVisitor/CodeDomConvertVisitor.cs +++ b/ICSharpCode.NRefactory.CSharp/OutputVisitor/CodeDomConvertVisitor.cs @@ -21,12 +21,12 @@ using System.CodeDom; using System.Collections.Generic; using System.IO; using System.Linq; +using ICSharpCode.NRefactory.CSharp.Refactoring; using ICSharpCode.NRefactory.CSharp.Resolver; using ICSharpCode.NRefactory.CSharp.TypeSystem; using ICSharpCode.NRefactory.PatternMatching; using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.NRefactory.TypeSystem.Implementation; namespace ICSharpCode.NRefactory.CSharp { @@ -40,14 +40,23 @@ namespace ICSharpCode.NRefactory.CSharp { //ICompilation compilation = MinimalResolveContext.Instance; CSharpAstResolver resolver; - bool useFullyQualifiedTypeNames; /// /// Gets/Sets whether the visitor should use fully-qualified type references. /// - public bool UseFullyQualifiedTypeNames { - get { return useFullyQualifiedTypeNames; } - set { useFullyQualifiedTypeNames = value; } + public bool UseFullyQualifiedTypeNames { get; set; } + + /// + /// Gets whether the visitor is allowed to produce snippet nodes for + /// code that cannot be converted. + /// The default is true. If this property is set to false, + /// unconvertible code will throw a NotSupportedException. + /// + public bool AllowSnippetNodes { get; set; } + + public CodeDomConvertVisitor() + { + this.AllowSnippetNodes = true; } /// @@ -136,7 +145,15 @@ namespace ICSharpCode.NRefactory.CSharp CodeTypeReference Convert(IType type) { - return new CodeTypeReference(type.ReflectionName); + if (type.Kind == TypeKind.Array) { + ArrayType a = (ArrayType)type; + return new CodeTypeReference(Convert(a.ElementType), a.Dimensions); + } else if (type is ParameterizedType) { + var pt = (ParameterizedType)type; + return new CodeTypeReference(pt.GetDefinition().ReflectionName, pt.TypeArguments.Select(Convert).ToArray()); + } else { + return new CodeTypeReference(type.ReflectionName); + } } CodeStatement Convert(Statement stmt) @@ -148,6 +165,8 @@ namespace ICSharpCode.NRefactory.CSharp { List result = new List(); foreach (Statement stmt in block.Statements) { + if (stmt is EmptyStatement) + continue; CodeStatement s = Convert(stmt); if (s != null) result.Add(s); @@ -160,6 +179,8 @@ namespace ICSharpCode.NRefactory.CSharp BlockStatement block = embeddedStatement as BlockStatement; if (block != null) { return ConvertBlock(block); + } else if (embeddedStatement is EmptyStatement) { + return new CodeStatement[0]; } CodeStatement s = Convert(embeddedStatement); if (s != null) @@ -170,6 +191,8 @@ namespace ICSharpCode.NRefactory.CSharp string MakeSnippet(AstNode node) { + if (!AllowSnippetNodes) + throw new NotSupportedException(); StringWriter w = new StringWriter(); CSharpOutputVisitor v = new CSharpOutputVisitor(w, FormattingOptionsFactory.CreateMono ()); node.AcceptVisitor(v); @@ -371,7 +394,7 @@ namespace ICSharpCode.NRefactory.CSharp TypeResolveResult trr = rr as TypeResolveResult; if (trr != null) { CodeTypeReference typeRef; - if (useFullyQualifiedTypeNames) { + if (UseFullyQualifiedTypeNames) { typeRef = Convert(trr.Type); } else { typeRef = new CodeTypeReference(identifierExpression.Identifier); @@ -380,7 +403,7 @@ namespace ICSharpCode.NRefactory.CSharp return new CodeTypeReferenceExpression(typeRef); } MethodGroupResolveResult mgrr = rr as MethodGroupResolveResult; - if (mgrr != null) { + if (mgrr != null || identifierExpression.TypeArguments.Any()) { return new CodeMethodReferenceExpression(new CodeThisReferenceExpression(), identifierExpression.Identifier, Convert(identifierExpression.TypeArguments)); } return new CodeVariableReferenceExpression(identifierExpression.Identifier); @@ -658,7 +681,7 @@ namespace ICSharpCode.NRefactory.CSharp CodeObject IAstVisitor.VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration) { CodeTypeDelegate d = new CodeTypeDelegate(delegateDeclaration.Name); - d.Attributes = ConvertMemberAttributes(delegateDeclaration.Modifiers); + d.Attributes = ConvertMemberAttributes(delegateDeclaration.Modifiers, EntityType.TypeDefinition); d.CustomAttributes.AddRange(Convert(delegateDeclaration.Attributes)); d.ReturnType = Convert(delegateDeclaration.ReturnType); d.Parameters.AddRange(Convert(delegateDeclaration.Parameters)); @@ -666,13 +689,15 @@ namespace ICSharpCode.NRefactory.CSharp return d; } - static MemberAttributes ConvertMemberAttributes(Modifiers modifiers) + MemberAttributes ConvertMemberAttributes(Modifiers modifiers, EntityType entityType) { MemberAttributes a = 0; if ((modifiers & Modifiers.Abstract) != 0) a |= MemberAttributes.Abstract; if ((modifiers & Modifiers.Sealed) != 0) a |= MemberAttributes.Final; + if (entityType != EntityType.TypeDefinition && (modifiers & (Modifiers.Abstract | Modifiers.Override | Modifiers.Virtual)) == 0) + a |= MemberAttributes.Final; if ((modifiers & Modifiers.Static) != 0) a |= MemberAttributes.Static; if ((modifiers & Modifiers.Override) != 0) @@ -719,7 +744,7 @@ namespace ICSharpCode.NRefactory.CSharp { //bool isNestedType = typeStack.Count > 0; CodeTypeDeclaration typeDecl = new CodeTypeDeclaration(typeDeclaration.Name); - typeDecl.Attributes = ConvertMemberAttributes(typeDeclaration.Modifiers); + typeDecl.Attributes = ConvertMemberAttributes(typeDeclaration.Modifiers, EntityType.TypeDefinition); typeDecl.CustomAttributes.AddRange(Convert(typeDeclaration.Attributes)); switch (typeDeclaration.ClassType) { @@ -809,7 +834,12 @@ namespace ICSharpCode.NRefactory.CSharp CodeObject IAstVisitor.VisitEmptyStatement(EmptyStatement emptyStatement) { - return null; + return EmptyStatement(); + } + + CodeStatement EmptyStatement() + { + return new CodeExpressionStatement(new CodeObjectCreateExpression(new CodeTypeReference(typeof(object)))); } CodeObject IAstVisitor.VisitExpressionStatement(ExpressionStatement expressionStatement) @@ -817,10 +847,55 @@ namespace ICSharpCode.NRefactory.CSharp AssignmentExpression assignment = expressionStatement.Expression as AssignmentExpression; if (assignment != null && assignment.Operator == AssignmentOperatorType.Assign) { return new CodeAssignStatement(Convert(assignment.Left), Convert(assignment.Right)); + } else if (assignment != null && CanBeDuplicatedForCompoundAssignment(assignment.Left)) { + CodeBinaryOperatorType op; + switch (assignment.Operator) { + case AssignmentOperatorType.Add: + op = CodeBinaryOperatorType.Add; + break; + case AssignmentOperatorType.Subtract: + op = CodeBinaryOperatorType.Subtract; + break; + case AssignmentOperatorType.Multiply: + op = CodeBinaryOperatorType.Multiply; + break; + case AssignmentOperatorType.Divide: + op = CodeBinaryOperatorType.Divide; + break; + case AssignmentOperatorType.Modulus: + op = CodeBinaryOperatorType.Modulus; + break; + case AssignmentOperatorType.BitwiseAnd: + op = CodeBinaryOperatorType.BitwiseAnd; + break; + case AssignmentOperatorType.BitwiseOr: + op = CodeBinaryOperatorType.BitwiseOr; + break; + default: + return MakeSnippetStatement(expressionStatement); + } + var cboe = new CodeBinaryOperatorExpression(Convert(assignment.Left), op, Convert(assignment.Right)); + return new CodeAssignStatement(Convert(assignment.Left), cboe); + } + UnaryOperatorExpression unary = expressionStatement.Expression as UnaryOperatorExpression; + if (unary != null && CanBeDuplicatedForCompoundAssignment(unary.Expression)) { + var op = unary.Operator; + if (op == UnaryOperatorType.Increment || op == UnaryOperatorType.PostIncrement) { + var cboe = new CodeBinaryOperatorExpression(Convert(unary.Expression), CodeBinaryOperatorType.Add, new CodePrimitiveExpression(1)); + return new CodeAssignStatement(Convert(unary.Expression), cboe); + } else if (op == UnaryOperatorType.Decrement || op == UnaryOperatorType.PostDecrement) { + var cboe = new CodeBinaryOperatorExpression(Convert(unary.Expression), CodeBinaryOperatorType.Subtract, new CodePrimitiveExpression(1)); + return new CodeAssignStatement(Convert(unary.Expression), cboe); + } } return new CodeExpressionStatement(Convert(expressionStatement.Expression)); } + bool CanBeDuplicatedForCompoundAssignment(Expression expr) + { + return expr is IdentifierExpression; + } + CodeObject IAstVisitor.VisitFixedStatement(FixedStatement fixedStatement) { return MakeSnippetStatement(fixedStatement); @@ -956,7 +1031,7 @@ namespace ICSharpCode.NRefactory.CSharp CodeObject IAstVisitor.VisitWhileStatement(WhileStatement whileStatement) { - return new CodeIterationStatement(null, Convert(whileStatement.Condition), null, ConvertEmbeddedStatement(whileStatement.EmbeddedStatement)); + return new CodeIterationStatement(EmptyStatement(), Convert(whileStatement.Condition), EmptyStatement(), ConvertEmbeddedStatement(whileStatement.EmbeddedStatement)); } CodeObject IAstVisitor.VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement) @@ -977,7 +1052,7 @@ namespace ICSharpCode.NRefactory.CSharp CodeObject IAstVisitor.VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) { CodeConstructor ctor = new CodeConstructor(); - ctor.Attributes = ConvertMemberAttributes(constructorDeclaration.Modifiers); + ctor.Attributes = ConvertMemberAttributes(constructorDeclaration.Modifiers, EntityType.Constructor); ctor.CustomAttributes.AddRange(Convert(constructorDeclaration.Attributes)); if (constructorDeclaration.Initializer.ConstructorInitializerType == ConstructorInitializerType.This) { ctor.ChainedConstructorArgs.AddRange(Convert(constructorDeclaration.Initializer.Arguments)); @@ -1019,7 +1094,7 @@ namespace ICSharpCode.NRefactory.CSharp } CodeMemberEvent e = new CodeMemberEvent(); - e.Attributes = ConvertMemberAttributes(eventDeclaration.Modifiers); + e.Attributes = ConvertMemberAttributes(eventDeclaration.Modifiers, EntityType.Event); e.CustomAttributes.AddRange(Convert(eventDeclaration.Attributes)); e.Name = vi.Name; e.Type = Convert(eventDeclaration.ReturnType); @@ -1037,7 +1112,7 @@ namespace ICSharpCode.NRefactory.CSharp { foreach (VariableInitializer vi in fieldDeclaration.Variables) { CodeMemberField f = new CodeMemberField(Convert(fieldDeclaration.ReturnType), vi.Name); - f.Attributes = ConvertMemberAttributes(fieldDeclaration.Modifiers); + f.Attributes = ConvertMemberAttributes(fieldDeclaration.Modifiers, EntityType.Field); f.CustomAttributes.AddRange(Convert(fieldDeclaration.Attributes)); f.InitExpression = ConvertVariableInitializer(vi.Initializer, fieldDeclaration.ReturnType); AddTypeMember(f); @@ -1048,7 +1123,7 @@ namespace ICSharpCode.NRefactory.CSharp CodeObject IAstVisitor.VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) { CodeMemberProperty p = new CodeMemberProperty(); - p.Attributes = ConvertMemberAttributes(indexerDeclaration.Modifiers); + p.Attributes = ConvertMemberAttributes(indexerDeclaration.Modifiers, EntityType.Indexer); p.CustomAttributes.AddRange(Convert(indexerDeclaration.Attributes)); p.Name = "Items"; p.PrivateImplementationType = Convert(indexerDeclaration.PrivateImplementationType); @@ -1069,7 +1144,7 @@ namespace ICSharpCode.NRefactory.CSharp CodeObject IAstVisitor.VisitMethodDeclaration(MethodDeclaration methodDeclaration) { CodeMemberMethod m = new CodeMemberMethod(); - m.Attributes = ConvertMemberAttributes(methodDeclaration.Modifiers); + m.Attributes = ConvertMemberAttributes(methodDeclaration.Modifiers, EntityType.Method); m.CustomAttributes.AddRange(Convert(methodDeclaration.Attributes.Where(a => a.AttributeTarget != "return"))); m.ReturnTypeCustomAttributes.AddRange(Convert(methodDeclaration.Attributes.Where(a => a.AttributeTarget == "return"))); @@ -1087,7 +1162,7 @@ namespace ICSharpCode.NRefactory.CSharp CodeObject IAstVisitor.VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) { CodeMemberMethod m = new CodeMemberMethod(); - m.Attributes = ConvertMemberAttributes(operatorDeclaration.Modifiers); + m.Attributes = ConvertMemberAttributes(operatorDeclaration.Modifiers, EntityType.Method); m.CustomAttributes.AddRange(Convert(operatorDeclaration.Attributes.Where(a => a.AttributeTarget != "return"))); m.ReturnTypeCustomAttributes.AddRange(Convert(operatorDeclaration.Attributes.Where(a => a.AttributeTarget == "return"))); @@ -1129,7 +1204,7 @@ namespace ICSharpCode.NRefactory.CSharp CodeObject IAstVisitor.VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) { CodeMemberProperty p = new CodeMemberProperty(); - p.Attributes = ConvertMemberAttributes(propertyDeclaration.Modifiers); + p.Attributes = ConvertMemberAttributes(propertyDeclaration.Modifiers, EntityType.Property); p.CustomAttributes.AddRange(Convert(propertyDeclaration.Attributes)); p.Name = propertyDeclaration.Name; p.PrivateImplementationType = Convert(propertyDeclaration.PrivateImplementationType); @@ -1181,7 +1256,7 @@ namespace ICSharpCode.NRefactory.CSharp CodeObject IAstVisitor.VisitSimpleType(SimpleType simpleType) { - if (useFullyQualifiedTypeNames) { + if (UseFullyQualifiedTypeNames) { IType type = Resolve(simpleType).Type; if (type.Kind != TypeKind.Unknown) return Convert(type); @@ -1198,7 +1273,7 @@ namespace ICSharpCode.NRefactory.CSharp tr.TypeArguments.AddRange(Convert(memberType.TypeArguments)); return tr; } - if (useFullyQualifiedTypeNames || memberType.IsDoubleColon) { + if (UseFullyQualifiedTypeNames || memberType.IsDoubleColon) { IType type = Resolve(memberType).Type; if (type.Kind != TypeKind.Unknown) return Convert(type); @@ -1309,7 +1384,7 @@ namespace ICSharpCode.NRefactory.CSharp return null; } - CodeObject IAstVisitor.VisitPatternPlaceholder(AstNode placeholder, ICSharpCode.NRefactory.PatternMatching.Pattern pattern) + CodeObject IAstVisitor.VisitPatternPlaceholder(AstNode placeholder, Pattern pattern) { return null; } diff --git a/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs b/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs index 8d750b714c..3f2c274438 100644 --- a/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs +++ b/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs @@ -3750,7 +3750,7 @@ namespace ICSharpCode.NRefactory.CSharp return AstType.Null; } - public AstNode ParseExpression (TextReader reader) + public Expression ParseExpression (TextReader reader) { var es = ParseStatements (new StringReader ("tmp = " + Environment.NewLine + reader.ReadToEnd () + ";"), -1).FirstOrDefault () as ExpressionStatement; if (es != null) { @@ -3758,7 +3758,7 @@ namespace ICSharpCode.NRefactory.CSharp if (ae != null) return ae.Right; } - return null; + return Expression.Null; } /// diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs index e818b6a5a0..9b5ec92a64 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs @@ -155,7 +155,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return Conversion.NullLiteralConversion; if (ImplicitReferenceConversion(fromType, toType, 0)) return Conversion.ImplicitReferenceConversion; - if (BoxingConversion(fromType, toType)) + if (IsBoxingConversion(fromType, toType)) return Conversion.BoxingConversion; if (fromType.Kind == TypeKind.Dynamic) return Conversion.ImplicitDynamicConversion; @@ -184,7 +184,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return true; if (ImplicitReferenceConversion(fromType, toType, 0)) return true; - if (BoxingConversion(fromType, toType) && !NullableType.IsNullable(fromType)) + if (IsBoxingConversion(fromType, toType) && !NullableType.IsNullable(fromType)) return true; if (ImplicitTypeParameterConversion(fromType, toType)) return true; @@ -398,6 +398,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver #endregion #region Implicit Reference Conversion + public bool IsImplicitReferenceConversion(IType fromType, IType toType) + { + return ImplicitReferenceConversion(fromType, toType, 0); + } + bool ImplicitReferenceConversion(IType fromType, IType toType, int subtypeCheckNestingDepth) { // C# 4.0 spec: §6.1.6 @@ -510,7 +515,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver #endregion #region Boxing Conversions - bool BoxingConversion(IType fromType, IType toType) + public bool IsBoxingConversion(IType fromType, IType toType) { // C# 4.0 spec: §6.1.7 fromType = NullableType.GetUnderlyingType(fromType); diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs index 6c3574b5d6..168bebca6b 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs @@ -390,7 +390,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver bool isNullable = NullableType.IsNullable(expression.Type); // the operator is overloadable: - OverloadResolution userDefinedOperatorOR = new OverloadResolution(compilation, new[] { expression }, conversions: conversions); + OverloadResolution userDefinedOperatorOR = CreateOverloadResolution(new[] { expression }); foreach (var candidate in GetUserDefinedOperatorCandidates(type, overloadableOperatorName)) { userDefinedOperatorOR.AddCandidate(candidate); } @@ -439,7 +439,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver default: throw new InvalidOperationException(); } - OverloadResolution builtinOperatorOR = new OverloadResolution(compilation, new[] { expression }, conversions: conversions); + OverloadResolution builtinOperatorOR = CreateOverloadResolution(new[] { expression }); foreach (var candidate in methodGroup) { builtinOperatorOR.AddCandidate(candidate); } @@ -569,7 +569,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver IType rhsType = NullableType.GetUnderlyingType(rhs.Type); // the operator is overloadable: - OverloadResolution userDefinedOperatorOR = new OverloadResolution(compilation, new[] { lhs, rhs }, conversions: conversions); + OverloadResolution userDefinedOperatorOR = CreateOverloadResolution(new[] { lhs, rhs }); HashSet userOperatorCandidates = new HashSet(); userOperatorCandidates.UnionWith(GetUserDefinedOperatorCandidates(lhsType, overloadableOperatorName)); userOperatorCandidates.UnionWith(GetUserDefinedOperatorCandidates(rhsType, overloadableOperatorName)); @@ -800,7 +800,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver default: throw new InvalidOperationException(); } - OverloadResolution builtinOperatorOR = new OverloadResolution(compilation, new[] { lhs, rhs }, conversions: conversions); + OverloadResolution builtinOperatorOR = CreateOverloadResolution(new[] { lhs, rhs }); foreach (var candidate in methodGroup) { builtinOperatorOR.AddCandidate(candidate); } @@ -1260,7 +1260,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver else if (rr.IsCompileTimeConstant && c != Conversion.None) return ResolveCast(targetType, rr); else - return new ConversionResolveResult(targetType, rr, c); + return new ConversionResolveResult(targetType, rr, c, checkForOverflow); } public ResolveResult ResolveCast(IType targetType, ResolveResult expression) @@ -1291,7 +1291,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } Conversion c = conversions.ExplicitConversion(expression, targetType); - return new ConversionResolveResult(targetType, expression, c); + return new ConversionResolveResult(targetType, expression, c, checkForOverflow); } internal object CSharpPrimitiveCast(TypeCode targetType, object input) @@ -1891,7 +1891,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver MethodGroupResolveResult mgrr = target as MethodGroupResolveResult; if (mgrr != null) { - OverloadResolution or = mgrr.PerformOverloadResolution(compilation, arguments, argumentNames, conversions: conversions); + OverloadResolution or = mgrr.PerformOverloadResolution(compilation, arguments, argumentNames, checkForOverflow: checkForOverflow, conversions: conversions); if (or.BestCandidate != null) { if (or.BestCandidate.IsStatic && !or.IsExtensionMethodInvocation && !(mgrr.TargetResult is TypeResolveResult)) return or.CreateResolveResult(new TypeResolveResult(mgrr.TargetResult.Type)); @@ -1914,7 +1914,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } IMethod invokeMethod = target.Type.GetDelegateInvokeMethod(); if (invokeMethod != null) { - OverloadResolution or = new OverloadResolution(compilation, arguments, argumentNames, conversions: conversions); + OverloadResolution or = CreateOverloadResolution(arguments, argumentNames); or.AddCandidate(invokeMethod); return new CSharpInvocationResolveResult( target, invokeMethod, //invokeMethod.ReturnType.Resolve(context), @@ -2003,6 +2003,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver variableName = variableName.Substring(1); return char.ToLower(variableName[0]) + variableName.Substring(1); } + + OverloadResolution CreateOverloadResolution(ResolveResult[] arguments, string[] argumentNames = null, IType[] typeArguments = null) + { + var or = new OverloadResolution(compilation, arguments, argumentNames, typeArguments, conversions); + or.CheckForOverflow = checkForOverflow; + return or; + } #endregion #region ResolveIndexer @@ -2035,7 +2042,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } // §7.6.6.2 Indexer access - OverloadResolution or = new OverloadResolution(compilation, arguments, argumentNames, conversions: conversions); + OverloadResolution or = CreateOverloadResolution(arguments, argumentNames); MemberLookup lookup = CreateMemberLookup(); var indexers = lookup.LookupIndexers(target.Type); or.AddMethodLists(indexers); @@ -2090,7 +2097,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (type.Kind == TypeKind.Delegate && arguments.Length == 1) { return Convert(arguments[0], type); } - OverloadResolution or = new OverloadResolution(compilation, arguments, argumentNames, conversions: conversions); + OverloadResolution or = CreateOverloadResolution(arguments, argumentNames); MemberLookup lookup = CreateMemberLookup(); foreach (IMethod ctor in type.GetConstructors()) { if (lookup.IsAccessible(ctor, allowProtectedAccess)) @@ -2197,6 +2204,27 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return Convert(input, boolean, c); } + /// + /// Converts the negated input to bool using the rules for boolean expressions. + /// Computes !(bool)input if the implicit cast to bool is valid; otherwise + /// computes input.operator false(). + /// + public ResolveResult ResolveConditionFalse(ResolveResult input) + { + if (input == null) + throw new ArgumentNullException("input"); + IType boolean = compilation.FindType(KnownTypeCode.Boolean); + Conversion c = conversions.ImplicitConversion(input, boolean); + if (!c.IsValid) { + var opFalse = input.Type.GetMethods(m => m.IsOperator && m.Name == "op_False").FirstOrDefault(); + if (opFalse != null) { + c = Conversion.UserDefinedImplicitConversion(opFalse, false); + return Convert(input, boolean, c); + } + } + return ResolveUnaryOperator(UnaryOperatorType.Not, Convert(input, boolean, c)); + } + public ResolveResult ResolveConditional(ResolveResult condition, ResolveResult trueExpression, ResolveResult falseExpression) { // C# 4.0 spec §7.14: Conditional operator diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs b/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs index 3d319ecfcf..87e9f46129 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs @@ -178,7 +178,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return string.Format("[{0} with {1} method(s)]", GetType().Name, this.Methods.Count()); } - public OverloadResolution PerformOverloadResolution(ICompilation compilation, ResolveResult[] arguments, string[] argumentNames = null, bool allowExtensionMethods = true, bool allowExpandingParams = true, CSharpConversions conversions = null) + public OverloadResolution PerformOverloadResolution(ICompilation compilation, ResolveResult[] arguments, string[] argumentNames = null, bool allowExtensionMethods = true, bool allowExpandingParams = true, bool checkForOverflow = false, CSharpConversions conversions = null) { Log.WriteLine("Performing overload resolution for " + this); Log.WriteCollection(" Arguments: ", arguments); @@ -186,6 +186,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver var typeArgumentArray = this.TypeArguments.ToArray(); OverloadResolution or = new OverloadResolution(compilation, arguments, argumentNames, typeArgumentArray, conversions); or.AllowExpandingParams = allowExpandingParams; + or.CheckForOverflow = checkForOverflow; or.AddMethodLists(methodLists); @@ -207,6 +208,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver var extOr = new OverloadResolution(compilation, extArguments, extArgumentNames, typeArgumentArray, conversions); extOr.AllowExpandingParams = allowExpandingParams; extOr.IsExtensionMethodInvocation = true; + extOr.CheckForOverflow = checkForOverflow; foreach (var g in extensionMethods) { foreach (var method in g) { diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs b/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs index 63c880bf81..d7f8da21a9 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs @@ -170,6 +170,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// public bool AllowExpandingParams { get; set; } + /// + /// Gets/Sets whether ConversionResolveResults created by this OverloadResolution + /// instance apply overflow checking. + /// The default value is false. + /// + public bool CheckForOverflow { get; set; } + /// /// Gets the arguments for which this OverloadResolution instance was created. /// @@ -801,9 +808,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver parameterType = SpecialType.UnknownType; } if (arguments[i].IsCompileTimeConstant && conversions[i] != Conversion.None) { - args[i] = new CSharpResolver(compilation).ResolveCast(parameterType, argument); + args[i] = new CSharpResolver(compilation).WithCheckForOverflow(CheckForOverflow).ResolveCast(parameterType, argument); } else { - args[i] = new ConversionResolveResult(parameterType, argument, conversions[i]); + args[i] = new ConversionResolveResult(parameterType, argument, conversions[i], CheckForOverflow); } } } diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs b/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs index 86300a0ab7..f4823ea7ae 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs @@ -1236,7 +1236,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (resolverEnabled) { ResolveResult input = Resolve(asExpression.Expression); var targetType = ResolveType(asExpression.Type); - return new ConversionResolveResult(targetType, input, Conversion.TryCast); + return new ConversionResolveResult(targetType, input, Conversion.TryCast, resolver.CheckForOverflow); } else { ScanChildren(asExpression); return null; @@ -1498,7 +1498,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver var addRR = memberLookup.Lookup(initializedObject, "Add", EmptyList.Instance, true); var mgrr = addRR as MethodGroupResolveResult; if (mgrr != null) { - OverloadResolution or = mgrr.PerformOverloadResolution(resolver.Compilation, addArguments, null, false, false, resolver.conversions); + OverloadResolution or = mgrr.PerformOverloadResolution(resolver.Compilation, addArguments, null, false, false, resolver.CheckForOverflow, resolver.conversions); var invocationRR = or.CreateResolveResult(initializedObject); StoreResult(aie, invocationRR); ProcessConversionsInInvocation(null, aie.Elements, invocationRR); @@ -3415,7 +3415,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// ResolveResult WrapResult(ResolveResult result) { - return new ConversionResolveResult(result.Type, result, Conversion.IdentityConversion); + return new ConversionResolveResult(result.Type, result, Conversion.IdentityConversion, resolver.CheckForOverflow); } ResolveResult IAstVisitor.VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause) @@ -3603,7 +3603,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver ProcessConversion(queryWhereClause.Condition, condition, conversionToBool, boolType); if (currentQueryResult != null) { if (conversionToBool != Conversion.IdentityConversion && conversionToBool != Conversion.None) { - condition = new ConversionResolveResult(boolType, condition, conversionToBool); + condition = new ConversionResolveResult(boolType, condition, conversionToBool, resolver.CheckForOverflow); } var methodGroup = resolver.ResolveMemberAccess(currentQueryResult, "Where", EmptyList.Instance); diff --git a/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeSystemConvertVisitor.cs b/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeSystemConvertVisitor.cs index 55c80e9c9a..d2a273dcc0 100644 --- a/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeSystemConvertVisitor.cs +++ b/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeSystemConvertVisitor.cs @@ -677,6 +677,7 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem if (accessor.IsNull) return null; var a = new DefaultUnresolvedMethod(currentTypeDefinition, prefix + p.Name); + a.AccessorOwner = p; a.Accessibility = GetAccessibility(accessor.Modifiers) ?? p.Accessibility; a.IsAbstract = p.IsAbstract; a.IsOverride = p.IsOverridable; diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeDomConvertVisitorTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeDomConvertVisitorTests.cs index 92efb0f156..52d55315bd 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeDomConvertVisitorTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeDomConvertVisitorTests.cs @@ -20,11 +20,11 @@ using System; using System.CodeDom; using System.CodeDom.Compiler; using System.IO; +using System.Linq; using System.Text.RegularExpressions; using ICSharpCode.NRefactory.CSharp.Resolver; using ICSharpCode.NRefactory.CSharp.TypeSystem; using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.NRefactory.TypeSystem.Implementation; using Microsoft.CSharp; using NUnit.Framework; @@ -45,138 +45,388 @@ namespace ICSharpCode.NRefactory.CSharp parsedFile.RootUsingScope.Usings.Add(MakeReference("System.Linq")); convertVisitor = new CodeDomConvertVisitor(); + convertVisitor.AllowSnippetNodes = false; convertVisitor.UseFullyQualifiedTypeNames = true; } - string Convert(Expression expr) + #region Helper methods + string ConvertHelper(AstNode node, Action action) { CSharpResolver resolver = new CSharpResolver(compilation); resolver = resolver.WithCurrentUsingScope(parsedFile.RootUsingScope.Resolve(compilation)); resolver = resolver.WithCurrentTypeDefinition(compilation.FindType(KnownTypeCode.Object).GetDefinition()); - var codeExpr = (CodeExpression)convertVisitor.Convert(expr, new CSharpAstResolver(resolver, expr, parsedFile)); + var codeObj = convertVisitor.Convert(node, new CSharpAstResolver(resolver, node)); StringWriter writer = new StringWriter(); writer.NewLine = " "; - new CSharpCodeProvider().GenerateCodeFromExpression(codeExpr, writer, new CodeGeneratorOptions { IndentString = " " }); - return Regex.Replace(writer.ToString(), @"\s+", " "); + action(new CSharpCodeProvider(), codeObj, writer, new CodeGeneratorOptions { IndentString = " " }); + return Regex.Replace(writer.ToString(), @"\s+", " ").Trim(); } + string ConvertExpression(Expression expr) + { + return ConvertHelper(expr, (p,obj,w,opt) => p.GenerateCodeFromExpression((CodeExpression)obj, w, opt)); + } + + string ConvertExpression(string code) + { + CSharpParser parser = new CSharpParser(); + var expr = parser.ParseExpression(new StringReader(code)); + Assert.IsFalse(parser.HasErrors); + return ConvertExpression(expr); + } + + string ConvertStatement(Statement statement) + { + return ConvertHelper(statement, (p,obj,w,opt) => p.GenerateCodeFromStatement((CodeStatement)obj, w, opt)); + } + + string ConvertStatement(string code) + { + CSharpParser parser = new CSharpParser(); + var expr = parser.ParseStatements(new StringReader(code)).Single(); + Assert.IsFalse(parser.HasErrors); + return ConvertStatement(expr); + } + + string ConvertMember(EntityDeclaration entity) + { + return ConvertHelper(entity, (p,obj,w,opt) => p.GenerateCodeFromMember((CodeTypeMember)obj, w, opt)); + } + + string ConvertMember(string code) + { + CSharpParser parser = new CSharpParser(); + var expr = parser.ParseTypeMembers(new StringReader(code)).Single(); + Assert.IsFalse(parser.HasErrors); + return ConvertMember(expr); + } + + string ConvertTypeDeclaration(EntityDeclaration decl) + { + return ConvertHelper(decl, (p,obj,w,opt) => p.GenerateCodeFromType((CodeTypeDeclaration)obj, w, opt)); + } + + string ConvertTypeDeclaration(string code) + { + CSharpParser parser = new CSharpParser(); + var cu = parser.Parse(new StringReader(code), "program.cs"); + Assert.IsFalse(parser.HasErrors); + return ConvertTypeDeclaration((EntityDeclaration)cu.Children.Single()); + } + #endregion + + #region Type References [Test] - public void CreateArray() + public void MultidimensionalArrayTypeReference() { - Assert.AreEqual("new int[10]", Convert( - new ArrayCreateExpression { - Type = new PrimitiveType("int"), - Arguments = { new PrimitiveExpression(10) } - })); + Assert.AreEqual("default(int[,][])", ConvertExpression("default(int[,][])")); } [Test] - public void CreateJaggedArray() + public void NestedTypeInGenericType() { - Assert.AreEqual("new int[10][]", Convert( - new ArrayCreateExpression { - Type = new PrimitiveType("int"), - Arguments = { new PrimitiveExpression(10) }, - AdditionalArraySpecifiers = { new ArraySpecifier() } - })); + Assert.AreEqual("default(System.Collections.Generic.List.Enumerator)", + ConvertExpression("default(List.Enumerator)")); + convertVisitor.UseFullyQualifiedTypeNames = false; + Assert.AreEqual("default(List.Enumerator)", + ConvertExpression("default(List.Enumerator)")); } + #endregion + #region Arrays [Test] + public void CreateArray() + { + Assert.AreEqual("new int[10]", ConvertExpression("new int[10]")); + } + + [Test, ExpectedException(typeof(NotSupportedException))] + public void CreateJaggedArray() + { + ConvertExpression("new int[10][]"); + } + + [Test, ExpectedException(typeof(NotSupportedException))] public void Create2DArray() { - Assert.AreEqual("new int[10, 20]", Convert( - new ArrayCreateExpression { - Type = new PrimitiveType("int"), - Arguments = { new PrimitiveExpression(10), new PrimitiveExpression(20) } - })); + ConvertExpression("new int[10, 20]"); } [Test] public void CreateImplicitlyTypedArray() { // implicitly-typed array not supported in CodeDom, so the conversion should infer the type - Assert.AreEqual("new int[] { 1, 2, 3}", Convert( - new ArrayCreateExpression { - AdditionalArraySpecifiers = { new ArraySpecifier() }, - Initializer = new ArrayInitializerExpression { - Elements = { - new PrimitiveExpression(1), - new PrimitiveExpression(2), - new PrimitiveExpression(3) - } - } - })); + Assert.AreEqual("new int[] { 1, 2, 3}", ConvertExpression("new [] { 1, 2, 3 }")); + Assert.AreEqual("new System.Collections.Generic.List[] { new System.Collections.Generic.List()}", + ConvertExpression("new [] { new List() }")); } - [Test] + [Test, ExpectedException(typeof(NotSupportedException))] public void Create2DImplicitlyTypedArray() { - Assert.AreEqual("new int[,] { { 1, 2 }, { 3, 4 }}", Convert( - new ArrayCreateExpression { - AdditionalArraySpecifiers = { new ArraySpecifier(2) }, - Initializer = new ArrayInitializerExpression { - Elements = { - new ArrayInitializerExpression(new PrimitiveExpression(1), new PrimitiveExpression(2)), - new ArrayInitializerExpression(new PrimitiveExpression(3), new PrimitiveExpression(4)) - } - } - })); + ConvertExpression("new [,] { { 1, 2 }, { 3, 4 }}"); } + #endregion + #region Operators [Test] - public void AdditionOperator() + public void ArithmeticOperators() { - Assert.AreEqual("(0 + 1)", Convert( - new BinaryOperatorExpression(new PrimitiveExpression(0), BinaryOperatorType.Add, new PrimitiveExpression(1)))); + Assert.AreEqual("(0 + 1)", ConvertExpression("0 + 1")); + Assert.AreEqual("(0 - 1)", ConvertExpression("0 - 1")); + Assert.AreEqual("(0 * 1)", ConvertExpression("0 * 1")); + Assert.AreEqual("(0 / 1)", ConvertExpression("0 / 1")); + Assert.AreEqual("(0 % 1)", ConvertExpression("0 % 1")); + Assert.AreEqual("(0 & 1)", ConvertExpression("0 & 1")); + Assert.AreEqual("(0 | 1)", ConvertExpression("0 | 1")); + Assert.AreEqual("(0 < 1)", ConvertExpression("0 < 1")); + Assert.AreEqual("(0 > 1)", ConvertExpression("0 > 1")); + Assert.AreEqual("(0 <= 1)", ConvertExpression("0 <= 1")); + Assert.AreEqual("(0 >= 1)", ConvertExpression("0 >= 1")); + Assert.AreEqual("(true && false)", ConvertExpression("true && false")); + Assert.AreEqual("(true || false)", ConvertExpression("true || false")); } [Test] public void EqualityOperator() { - Assert.AreEqual("(0 == 1)", Convert( - new BinaryOperatorExpression(new PrimitiveExpression(0), BinaryOperatorType.Equality, new PrimitiveExpression(1)))); + Assert.AreEqual("(0 == 1)", ConvertExpression("0 == 1")); + Assert.AreEqual("(default(object) == null)", ConvertExpression("default(object) == null")); } [Test] public void InEqualityOperator() { - Assert.AreEqual("((0 == 1) == false)", Convert( - new BinaryOperatorExpression(new PrimitiveExpression(0), BinaryOperatorType.InEquality, new PrimitiveExpression(1)))); + Assert.AreEqual("((0 == 1) == false)", ConvertExpression("0 != 1")); + Assert.AreEqual("(default(object) != null)", ConvertExpression("default(object) != null")); } [Test] - public void ReferenceInEqualityOperator() + public void UnaryOperators() { - Assert.AreEqual("(default(object) != null)", Convert( - new BinaryOperatorExpression(new DefaultValueExpression(new PrimitiveType("object")), BinaryOperatorType.InEquality, new NullReferenceExpression()))); + Assert.AreEqual("(a == false)", ConvertExpression("!a")); + Assert.AreEqual("(0 - a)", ConvertExpression("-a")); + Assert.AreEqual("a", ConvertExpression("+a")); } + [Test] + public void Cast() + { + Assert.AreEqual("((double)(0))", ConvertExpression("(double)0")); + } + #endregion + + #region Member Access [Test] public void StaticProperty() { - Assert.AreEqual("System.Environment.TickCount", Convert( - new IdentifierExpression("Environment").Member("TickCount"))); + Assert.AreEqual("System.Environment.TickCount", ConvertExpression("Environment.TickCount")); } [Test] public void InstanceMethodInvocation() { - Assert.AreEqual("this.Equals(null)", - Convert(new IdentifierExpression("Equals").Invoke(new NullReferenceExpression()))); + Assert.AreEqual("this.Equals(null)", ConvertExpression("Equals(null)")); } [Test] public void StaticMethodInvocation() { - Assert.AreEqual("object.Equals(null, null)", - Convert(new IdentifierExpression("Equals").Invoke(new NullReferenceExpression(), new NullReferenceExpression()))); + Assert.AreEqual("object.Equals(null, null)", ConvertExpression("Equals(null, null)")); + } + + [Test] + public void BaseMemberAccess() + { + Assert.AreEqual("base.X", ConvertExpression("base.X")); + Assert.AreEqual("base[i]", ConvertExpression("base[i]")); + } + + [Test] + public void GenericMethodReference() + { + Assert.AreEqual("this.Stuff", ConvertExpression("this.Stuff")); + Assert.AreEqual("this.Stuff", ConvertExpression("Stuff")); + } + + [Test] + public void ByReferenceCall() + { + Assert.AreEqual("a.Call(ref x, out y, z)", ConvertExpression("a.Call(ref x, out y, z)")); + } + + [Test] + public void MemberAccessOnType() + { + Assert.AreEqual("string.Empty", ConvertExpression("string.Empty")); + } + #endregion + + #region Statements + [Test] + public void MethodInvocationStatement() + { + Assert.AreEqual("a.SomeMethod();", ConvertStatement("a.SomeMethod();")); + } + + [Test] + public void Assignment() + { + Assert.AreEqual("a = 1;", ConvertStatement("a = 1;")); + } + + [Test, ExpectedException(typeof(NotSupportedException))] + public void AssignmentNotSupportedInExpression() + { + ConvertStatement("a = b = 1;"); + } + + [Test] + public void BlockStatement() + { + Assert.AreEqual("if (true) { a = 1; b = 2; }", + ConvertStatement("{ a = 1; b = 2; }")); + } + + [Test] + public void CompoundAssign() + { + Assert.AreEqual("a = (a + 1);", ConvertStatement("a += 1;")); + Assert.AreEqual("a = (a - 1);", ConvertStatement("a -= 1;")); + Assert.AreEqual("a = (a * 1);", ConvertStatement("a *= 1;")); + Assert.AreEqual("a = (a / 1);", ConvertStatement("a /= 1;")); + Assert.AreEqual("a = (a % 1);", ConvertStatement("a %= 1;")); + Assert.AreEqual("a = (a & 1);", ConvertStatement("a &= 1;")); + Assert.AreEqual("a = (a | 1);", ConvertStatement("a |= 1;")); + } + + [Test] + public void Increment() + { + Assert.AreEqual("a = (a + 1);", ConvertStatement("a++;")); + Assert.AreEqual("a = (a + 1);", ConvertStatement("++a;")); + Assert.AreEqual("a = (a - 1);", ConvertStatement("a--;")); + Assert.AreEqual("a = (a - 1);", ConvertStatement("--a;")); + } + + [Test] + public void ForLoop() + { + Assert.AreEqual("for (int i = 0; (i < 10); i = (i + 1)) { }", + ConvertStatement("for (int i = 0; i < 10; i++) {}")); + } + + [Test] + public void WhileLoop() + { + Assert.AreEqual("for (new object(); (i < 10); new object()) { }", + ConvertStatement("while (i < 10);")); + } + + [Test] + public void VariableDeclarationWithArrayInitializer() + { + Assert.AreEqual("int[] nums = new int[] { 1, 2};", + ConvertStatement("int[] nums = { 1, 2 };")); + } + + [Test] + public void TryCatch() + { + Assert.AreEqual("try { a = 1; } catch (System.Exception ex) { ex.ToString(); }", + ConvertStatement("try { a = 1; } catch (Exception ex) { ex.ToString(); }")); + } + + [Test] + public void TryEmptyCatch() + { + Assert.AreEqual("try { a = 1; } catch (System.Exception ) { }", + ConvertStatement("try { a = 1; } catch (Exception) { }")); + } + + [Test] + public void TryFinally() + { + Assert.AreEqual("try { a = 1; } finally { a = 0; }", + ConvertStatement("try { a = 1; } finally { a = 0; }")); + } + #endregion + + #region Type Members + [Test] + public void MethodParameterNamedValue() + { + Assert.AreEqual("void M(string value) { System.Console.WriteLine(value); }", + ConvertMember("void M(string value) { Console.WriteLine(value); }")); + } + + [Test] + public void ValueInProperty() + { + Assert.AreEqual("string P { set { System.Console.WriteLine(value); } }", + ConvertMember("string P { set { Console.WriteLine(value); } }")); + } + + [Test] + public void MethodWithAttribute() + { + Assert.AreEqual("[Test()] void MethodWithAttribute() { }", + ConvertMember("[Test] void MethodWithAttribute() { }")); + } + + [Test] + public void PublicNonVirtualMethod() + { + Assert.AreEqual("public void Method() { }", + ConvertMember("public void Method() { }")); + } + + [Test] + public void PublicVirtualMethod() + { + Assert.AreEqual("public virtual void Method() { }", + ConvertMember("public virtual void Method() { }")); + } + + [Test] + public void NestedClass() + { + Assert.AreEqual("public class Outer { public class Inner { } }", + ConvertTypeDeclaration("class Outer { class Inner { } }")); + } + + [Test] + public void Constructor() + { + string code = "public class Test : Base { public Test(string x) : base(x) { } }"; + Assert.AreEqual(code, ConvertTypeDeclaration(code)); + } + + [Test] + public void Enum() + { + string code = "public enum E { [Description(\"Text\")] None, B = 2, }"; + Assert.AreEqual(code, ConvertTypeDeclaration(code)); + } + + [Test] + public void Field() + { + Assert.AreEqual("public class X {" + + " int A;" + + " int B; }", + ConvertMember("public class X { int A, B; }")); } [Test] - public void NotOperator() + public void Event() { - Assert.AreEqual("(a == false)", Convert(new UnaryOperatorExpression(UnaryOperatorType.Not, new IdentifierExpression("a")))); + Assert.AreEqual("public class X {" + + " protected event System.EventHandler A;" + + " protected event System.EventHandler B; }", + ConvertMember("public class X { protected event EventHandler A, B; }")); } + #endregion } } diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs index d130b6b4f6..580138eae6 100644 --- a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs @@ -72,6 +72,8 @@ namespace ICSharpCode.NRefactory.TypeSystem.TestCase public NestedEnum EnumField; + public A Property { get; set; } + public enum NestedEnum { EnumMember } diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs index 4586652ff8..6f76812ac4 100644 --- a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs @@ -351,6 +351,7 @@ namespace ICSharpCode.NRefactory.TypeSystem Assert.AreEqual(Accessibility.Public, p.Getter.Accessibility); Assert.AreEqual(new[] { "index" }, p.Getter.Parameters.Select(x => x.Name).ToArray()); Assert.AreEqual("System.String", p.Getter.ReturnType.ReflectionName); + Assert.AreEqual(p, p.Getter.AccessorOwner); } [Test] @@ -365,6 +366,16 @@ namespace ICSharpCode.NRefactory.TypeSystem Assert.AreEqual(TypeKind.Void, p.Setter.ReturnType.Kind); } + [Test] + public void GenericPropertyGetter() + { + var type = compilation.FindType(typeof(GenericClass)); + var prop = type.GetProperties(p => p.Name == "Property").Single(); + Assert.AreEqual("System.String", prop.Getter.ReturnType.ReflectionName); + Assert.IsTrue(prop.Getter.IsAccessor); + Assert.AreEqual(prop, prop.Getter.AccessorOwner); + } + [Test] public void EnumTest() { diff --git a/ICSharpCode.NRefactory/Editor/IDocument.cs b/ICSharpCode.NRefactory/Editor/IDocument.cs index 4201ce4484..f9a793b911 100644 --- a/ICSharpCode.NRefactory/Editor/IDocument.cs +++ b/ICSharpCode.NRefactory/Editor/IDocument.cs @@ -109,6 +109,18 @@ namespace ICSharpCode.NRefactory.Editor /// void Insert(int offset, string text); + /// + /// Inserts text. + /// + /// The offset at which the text is inserted. + /// The new text. + /// + /// Anchors positioned exactly at the insertion offset will move according to their movement type. + /// For AnchorMovementType.Default, they will move behind the inserted text. + /// The caret will also move behind the inserted text. + /// + void Insert(int offset, ITextSource text); + /// /// Inserts text. /// @@ -121,6 +133,18 @@ namespace ICSharpCode.NRefactory.Editor /// void Insert(int offset, string text, AnchorMovementType defaultAnchorMovementType); + /// + /// Inserts text. + /// + /// The offset at which the text is inserted. + /// The new text. + /// + /// Anchors positioned exactly at the insertion offset will move according to the anchor's movement type. + /// For AnchorMovementType.Default, they will move according to the movement type specified by this parameter. + /// The caret will also move according to the parameter. + /// + void Insert(int offset, ITextSource text, AnchorMovementType defaultAnchorMovementType); + /// /// Removes text. /// @@ -136,6 +160,14 @@ namespace ICSharpCode.NRefactory.Editor /// The new text. void Replace(int offset, int length, string newText); + /// + /// Replaces text. + /// + /// The starting offset of the text to be replaced. + /// The length of the text to be replaced. + /// The new text. + void Replace(int offset, int length, ITextSource newText); + /// /// Make the document combine the following actions into a single /// action for undo purposes. diff --git a/ICSharpCode.NRefactory/Editor/ReadOnlyDocument.cs b/ICSharpCode.NRefactory/Editor/ReadOnlyDocument.cs index a4d76a8394..2a36ccda33 100644 --- a/ICSharpCode.NRefactory/Editor/ReadOnlyDocument.cs +++ b/ICSharpCode.NRefactory/Editor/ReadOnlyDocument.cs @@ -257,6 +257,21 @@ namespace ICSharpCode.NRefactory.Editor throw new NotSupportedException(); } + void IDocument.Insert(int offset, ITextSource text) + { + throw new NotImplementedException(); + } + + void IDocument.Insert(int offset, ITextSource text, AnchorMovementType defaultAnchorMovementType) + { + throw new NotImplementedException(); + } + + void IDocument.Replace(int offset, int length, ITextSource newText) + { + throw new NotImplementedException(); + } + void IDocument.StartUndoableAction() { } diff --git a/ICSharpCode.NRefactory/Editor/StringBuilderDocument.cs b/ICSharpCode.NRefactory/Editor/StringBuilderDocument.cs index 79553a4136..f69070a196 100644 --- a/ICSharpCode.NRefactory/Editor/StringBuilderDocument.cs +++ b/ICSharpCode.NRefactory/Editor/StringBuilderDocument.cs @@ -104,17 +104,35 @@ namespace ICSharpCode.NRefactory.Editor Replace(offset, 0, text); } + /// + public void Insert(int offset, ITextSource text) + { + if (text == null) + throw new ArgumentNullException("text"); + Replace(offset, 0, text.Text); + } + /// public void Insert(int offset, string text, AnchorMovementType defaultAnchorMovementType) { if (offset < 0 || offset > this.TextLength) throw new ArgumentOutOfRangeException("offset"); + if (text == null) + throw new ArgumentNullException("text"); if (defaultAnchorMovementType == AnchorMovementType.BeforeInsertion) PerformChange(new InsertionWithMovementBefore(offset, text)); else Replace(offset, 0, text); } + /// + public void Insert(int offset, ITextSource text, AnchorMovementType defaultAnchorMovementType) + { + if (text == null) + throw new ArgumentNullException("text"); + Insert(offset, text.Text, defaultAnchorMovementType); + } + [Serializable] sealed class InsertionWithMovementBefore : TextChangeEventArgs { @@ -149,6 +167,14 @@ namespace ICSharpCode.NRefactory.Editor PerformChange(new TextChangeEventArgs(offset, b.ToString(offset, length), newText)); } + /// + public void Replace(int offset, int length, ITextSource newText) + { + if (newText == null) + throw new ArgumentNullException("newText"); + Replace(offset, length, newText.Text); + } + bool isInChange; void PerformChange(TextChangeEventArgs change) @@ -166,7 +192,7 @@ namespace ICSharpCode.NRefactory.Editor documentSnapshot = null; cachedText = null; b.Remove(change.Offset, change.RemovalLength); - b.Insert(change.Offset, change.InsertedText); + b.Insert(change.Offset, change.InsertedText.Text); versionProvider.AppendChange(change); // Update anchors and fire Deleted events diff --git a/ICSharpCode.NRefactory/Editor/StringTextSource.cs b/ICSharpCode.NRefactory/Editor/StringTextSource.cs index 3a24db3646..36780954bd 100644 --- a/ICSharpCode.NRefactory/Editor/StringTextSource.cs +++ b/ICSharpCode.NRefactory/Editor/StringTextSource.cs @@ -27,6 +27,11 @@ namespace ICSharpCode.NRefactory.Editor [Serializable] public class StringTextSource : ITextSource { + /// + /// Gets a text source containing the empty string. + /// + public static readonly StringTextSource Empty = new StringTextSource(string.Empty); + readonly string text; readonly ITextSourceVersion version; @@ -69,7 +74,7 @@ namespace ICSharpCode.NRefactory.Editor /// public ITextSource CreateSnapshot() { - return this; // StringTextBuffer is immutable + return this; // StringTextSource is immutable } /// diff --git a/ICSharpCode.NRefactory/Editor/TextChangeEventArgs.cs b/ICSharpCode.NRefactory/Editor/TextChangeEventArgs.cs index 936de37aa4..f1eeb4d27f 100644 --- a/ICSharpCode.NRefactory/Editor/TextChangeEventArgs.cs +++ b/ICSharpCode.NRefactory/Editor/TextChangeEventArgs.cs @@ -28,8 +28,8 @@ namespace ICSharpCode.NRefactory.Editor public class TextChangeEventArgs : EventArgs { readonly int offset; - readonly string removedText; - readonly string insertedText; + readonly ITextSource removedText; + readonly ITextSource insertedText; /// /// The offset at which the change occurs. @@ -41,7 +41,7 @@ namespace ICSharpCode.NRefactory.Editor /// /// The text that was removed. /// - public string RemovedText { + public ITextSource RemovedText { get { return removedText; } } @@ -49,13 +49,13 @@ namespace ICSharpCode.NRefactory.Editor /// The number of characters removed. /// public int RemovalLength { - get { return removedText.Length; } + get { return removedText.TextLength; } } /// /// The text that was inserted. /// - public string InsertedText { + public ITextSource InsertedText { get { return insertedText; } } @@ -63,7 +63,7 @@ namespace ICSharpCode.NRefactory.Editor /// The number of characters inserted. /// public int InsertionLength { - get { return insertedText.Length; } + get { return insertedText.TextLength; } } /// @@ -71,9 +71,23 @@ namespace ICSharpCode.NRefactory.Editor /// public TextChangeEventArgs(int offset, string removedText, string insertedText) { + if (offset < 0) + throw new ArgumentOutOfRangeException("offset", offset, "offset must not be negative"); this.offset = offset; - this.removedText = removedText ?? string.Empty; - this.insertedText = insertedText ?? string.Empty; + this.removedText = removedText != null ? new StringTextSource(removedText) : StringTextSource.Empty; + this.insertedText = insertedText != null ? new StringTextSource(insertedText) : StringTextSource.Empty; + } + + /// + /// Creates a new TextChangeEventArgs object. + /// + public TextChangeEventArgs(int offset, ITextSource removedText, ITextSource insertedText) + { + if (offset < 0) + throw new ArgumentOutOfRangeException("offset", offset, "offset must not be negative"); + this.offset = offset; + this.removedText = removedText ?? StringTextSource.Empty; + this.insertedText = insertedText ?? StringTextSource.Empty; } /// diff --git a/ICSharpCode.NRefactory/Semantics/ConversionResolveResult.cs b/ICSharpCode.NRefactory/Semantics/ConversionResolveResult.cs index d015ddd3d4..303118bf00 100644 --- a/ICSharpCode.NRefactory/Semantics/ConversionResolveResult.cs +++ b/ICSharpCode.NRefactory/Semantics/ConversionResolveResult.cs @@ -27,6 +27,11 @@ namespace ICSharpCode.NRefactory.Semantics public readonly ResolveResult Input; public readonly Conversion Conversion; + /// + /// For numeric conversions, specifies whether overflow checking is enabled. + /// + public readonly bool CheckForOverflow; + public ConversionResolveResult(IType targetType, ResolveResult input, Conversion conversion) : base(targetType) { @@ -38,6 +43,12 @@ namespace ICSharpCode.NRefactory.Semantics this.Conversion = conversion; } + public ConversionResolveResult(IType targetType, ResolveResult input, Conversion conversion, bool checkForOverflow) + : this(targetType, input, conversion) + { + this.CheckForOverflow = checkForOverflow; + } + public override bool IsError { get { return !Conversion.IsValid; } } diff --git a/ICSharpCode.NRefactory/Semantics/ErrorResolveResult.cs b/ICSharpCode.NRefactory/Semantics/ErrorResolveResult.cs index 15942f7a8a..da68da167a 100644 --- a/ICSharpCode.NRefactory/Semantics/ErrorResolveResult.cs +++ b/ICSharpCode.NRefactory/Semantics/ErrorResolveResult.cs @@ -35,8 +35,18 @@ namespace ICSharpCode.NRefactory.Semantics { } + public ErrorResolveResult(IType type, string message, TextLocation location) : base(type) + { + this.Message = message; + this.Location = location; + } + public override bool IsError { get { return true; } } + + public string Message { get; private set; } + + public TextLocation Location { get; private set; } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs index 7bc866c501..15530707a5 100644 --- a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs +++ b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs @@ -1646,11 +1646,17 @@ namespace ICSharpCode.NRefactory.TypeSystem #region Read Method [CLSCompliant(false)] public IUnresolvedMethod ReadMethod(MethodDefinition method, IUnresolvedTypeDefinition parentType, EntityType methodType = EntityType.Method) + { + return ReadMethod(method, parentType, null, methodType); + } + + IUnresolvedMethod ReadMethod(MethodDefinition method, IUnresolvedTypeDefinition parentType, IUnresolvedMember accessorOwner, EntityType methodType = EntityType.Method) { if (method == null) return null; DefaultUnresolvedMethod m = new DefaultUnresolvedMethod(parentType, method.Name); m.EntityType = methodType; + m.AccessorOwner = accessorOwner; if (method.HasGenericParameters) { for (int i = 0; i < method.GenericParameters.Count; i++) { if (method.GenericParameters[i].Position != i) @@ -1876,8 +1882,8 @@ namespace ICSharpCode.NRefactory.TypeSystem TranslateModifiers(property.GetMethod ?? property.SetMethod, p); p.ReturnType = ReadTypeReference(property.PropertyType, typeAttributes: property); - p.Getter = ReadMethod(property.GetMethod, parentType); - p.Setter = ReadMethod(property.SetMethod, parentType); + p.Getter = ReadMethod(property.GetMethod, parentType, p); + p.Setter = ReadMethod(property.SetMethod, parentType, p); if (property.HasParameters) { foreach (ParameterDefinition par in property.Parameters) { @@ -1904,9 +1910,9 @@ namespace ICSharpCode.NRefactory.TypeSystem TranslateModifiers(ev.AddMethod, e); e.ReturnType = ReadTypeReference(ev.EventType, typeAttributes: ev); - e.AddAccessor = ReadMethod(ev.AddMethod, parentType); - e.RemoveAccessor = ReadMethod(ev.RemoveMethod, parentType); - e.InvokeAccessor = ReadMethod(ev.InvokeMethod, parentType); + e.AddAccessor = ReadMethod(ev.AddMethod, parentType, e); + e.RemoveAccessor = ReadMethod(ev.RemoveMethod, parentType, e); + e.InvokeAccessor = ReadMethod(ev.InvokeMethod, parentType, e); AddAttributes(ev, e); diff --git a/ICSharpCode.NRefactory/TypeSystem/IMethod.cs b/ICSharpCode.NRefactory/TypeSystem/IMethod.cs index c3fd1b7720..74a7cc6b0b 100644 --- a/ICSharpCode.NRefactory/TypeSystem/IMethod.cs +++ b/ICSharpCode.NRefactory/TypeSystem/IMethod.cs @@ -39,6 +39,12 @@ namespace ICSharpCode.NRefactory.TypeSystem bool IsPartialMethodDeclaration { get; } bool IsPartialMethodImplementation { get; } + /// + /// If this method is an accessor, returns a reference to the corresponding property/event. + /// Otherwise, returns null. + /// + IUnresolvedMember AccessorOwner { get; } + /// /// Resolves the member. /// @@ -75,5 +81,16 @@ namespace ICSharpCode.NRefactory.TypeSystem bool IsConstructor { get; } bool IsDestructor { get; } bool IsOperator { get; } + + /// + /// Gets whether the method is a property/event accessor. + /// + bool IsAccessor { get; } + + /// + /// If this method is an accessor, returns the corresponding property/event. + /// Otherwise, returns null. + /// + IMember AccessorOwner { get; } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedMethod.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedMethod.cs index 57ddbbfa48..6a20f62c21 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedMethod.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedMethod.cs @@ -190,6 +190,20 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation public bool IsOperator { get { return ((IUnresolvedMethod)unresolved).IsOperator; } } + + public bool IsAccessor { + get { return ((IUnresolvedMethod)unresolved).AccessorOwner != null; } + } + + public IMember AccessorOwner { + get { + var reference = ((IUnresolvedMethod)unresolved).AccessorOwner; + if (reference != null) + return reference.Resolve(context); + else + return null; + } + } public override IMemberReference ToMemberReference() { diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedMethod.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedMethod.cs index f06815297a..8b0e16d345 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedMethod.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedMethod.cs @@ -32,6 +32,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation IList returnTypeAttributes; IList typeParameters; IList parameters; + IUnresolvedMember accessorOwner; protected override void FreezeInternal() { @@ -125,6 +126,14 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } } + public IUnresolvedMember AccessorOwner { + get { return accessorOwner; } + set { + ThrowIfFrozen(); + accessorOwner = value; + } + } + public override string ToString() { StringBuilder b = new StringBuilder("["); @@ -150,6 +159,29 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation public override IMember Resolve(ITypeResolveContext context) { + if (accessorOwner != null) { + var owner = accessorOwner.Resolve(context); + if (owner != null) { + IProperty p = owner as IProperty; + if (p != null) { + if (p.CanGet && p.Getter.Name == this.Name) + return p.Getter; + if (p.CanSet && p.Setter.Name == this.Name) + return p.Setter; + } + IEvent e = owner as IEvent; + if (e != null) { + if (e.CanAdd && e.AddAccessor.Name == this.Name) + return e.AddAccessor; + if (e.CanRemove && e.RemoveAccessor.Name == this.Name) + return e.RemoveAccessor; + if (e.CanInvoke && e.InvokeAccessor.Name == this.Name) + return e.InvokeAccessor; + } + } + return null; + } + ITypeReference interfaceTypeReference = null; if (this.IsExplicitInterfaceImplementation && this.ExplicitInterfaceImplementations.Count == 1) interfaceTypeReference = this.ExplicitInterfaceImplementations[0].DeclaringTypeReference; diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMember.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMember.cs index 53fd6d350c..f9ba5382f5 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMember.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMember.cs @@ -101,10 +101,13 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation if (accessorDefinition == null) return null; var result = LazyInit.VolatileRead(ref cachingField); - if (result != null) + if (result != null) { return result; - else - return LazyInit.GetOrSet(ref cachingField, new SpecializedMethod(accessorDefinition, substitution)); + } else { + var sm = new SpecializedMethod(accessorDefinition, substitution); + //sm.AccessorOwner = this; + return LazyInit.GetOrSet(ref cachingField, sm); + } } /// diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs index ac3fdb3d8c..79b5d10fa7 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SpecializedMethod.cs @@ -21,7 +21,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; - +using System.Threading; using ICSharpCode.NRefactory.Utils; namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -121,6 +121,27 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation get { return methodDefinition.IsOperator; } } + public bool IsAccessor { + get { return methodDefinition.IsAccessor; } + } + + IMember accessorOwner; + + public IMember AccessorOwner { + get { + var result = LazyInit.VolatileRead(ref accessorOwner); + if (result != null) { + return result; + } else { + result = SpecializedMember.Create(methodDefinition.AccessorOwner, this.Substitution); + return LazyInit.GetOrSet(ref accessorOwner, result); + } + } + internal set { + accessorOwner = value; + } + } + public override IMemberReference ToMemberReference() { // Pass the MethodTypeArguments to the SpecializingMemberReference only if diff --git a/ICSharpCode.NRefactory/TypeSystem/KnownTypeReference.cs b/ICSharpCode.NRefactory/TypeSystem/KnownTypeReference.cs index 53277ee713..8abb14a7a5 100644 --- a/ICSharpCode.NRefactory/TypeSystem/KnownTypeReference.cs +++ b/ICSharpCode.NRefactory/TypeSystem/KnownTypeReference.cs @@ -101,6 +101,12 @@ namespace ICSharpCode.NRefactory.TypeSystem IEnumerableOfT, /// System.Collections.Generic.IEnumerator{T} IEnumeratorOfT, + /// System.Collections.Generic.ICollection + ICollection, + /// System.Collections.Generic.ICollection{T} + ICollectionOfT, + /// System.Collections.Generic.IList + IList, /// System.Collections.Generic.IList{T} IListOfT, /// System.Collections.Generic.IReadOnlyList{T} @@ -158,7 +164,11 @@ namespace ICSharpCode.NRefactory.TypeSystem new KnownTypeReference(KnownTypeCode.IEnumerator, "System.Collections", "IEnumerator"), new KnownTypeReference(KnownTypeCode.IEnumerableOfT, "System.Collections.Generic", "IEnumerable", 1), new KnownTypeReference(KnownTypeCode.IEnumeratorOfT, "System.Collections.Generic", "IEnumerator", 1), + new KnownTypeReference(KnownTypeCode.ICollection, "System.Collections", "ICollection"), + new KnownTypeReference(KnownTypeCode.ICollectionOfT, "System.Collections.Generic", "ICollection", 1), + new KnownTypeReference(KnownTypeCode.IList, "System.Collections", "IList"), new KnownTypeReference(KnownTypeCode.IListOfT, "System.Collections.Generic", "IList", 1), + new KnownTypeReference(KnownTypeCode.IReadOnlyListOfT, "System.Collections.Generic", "IReadOnlyList", 1), new KnownTypeReference(KnownTypeCode.Task, "System.Threading.Tasks", "Task"), new KnownTypeReference(KnownTypeCode.TaskOfT, "System.Threading.Tasks", "Task", 1, baseType: KnownTypeCode.Task),