context: // Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
  // 
  // Permission is hereby granted, free of charge, to any person obtaining a copy of this
  // software and associated documentation files (the "Software"), to deal in the Software
  // without restriction, including without limitation the rights to use, copy, modify, merge,
  // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  // to whom the Software is furnished to do so, subject to the following conditions:
  // 
  // The above copyright notice and this permission notice shall be included in all copies or
  // substantial portions of the Software.
  // 
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
  // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  // DEALINGS IN THE SOFTWARE.
  
  using System;
  using 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;
  
  namespace ICSharpCode.NRefactory.CSharp
  {
  	/// <summary>
  	/// Converts from C# AST to CodeDom.
  	/// </summary>
  	/// <remarks>
  	/// The conversion is intended for use in the SharpDevelop forms designer.
  	/// </remarks>
  	public class CodeDomConvertVisitor : IAstVisitor<CodeObject>
  	{
  		CSharpAstResolver resolver;
  		
  		/// <summary>
  		/// Gets/Sets whether the visitor should convert short type names into
  		/// fully qualified type names.
  		/// The default is <c>false</c>.
  		/// </summary>
  		public bool UseFullyQualifiedTypeNames { get; set; }
  		
  		/// <summary>
  		/// Gets whether the visitor is allowed to produce snippet nodes for
  		/// code that cannot be converted.
  		/// The default is <c>true</c>. If this property is set to <c>false</c>,
  		/// unconvertible code will throw a NotSupportedException.
  		/// </summary>
  		public bool AllowSnippetNodes { get; set; }
  		
  		public CodeDomConvertVisitor()
  		{
  			this.AllowSnippetNodes = true;
  		}
  		
  		/// <summary>
  		/// Converts a syntax tree to CodeDom.
  		/// </summary>
  		/// <param name="syntaxTree">The input syntax tree.</param>
  		/// <param name="compilation">The current compilation.</param>
  		/// <param name="unresolvedFile">CSharpUnresolvedFile, used for resolving.</param>
  		/// <returns>Converted CodeCompileUnit</returns>
  		/// <remarks>
  		/// This conversion process requires a resolver because it needs to distinguish field/property/event references etc.
  		/// </remarks>
  		public CodeCompileUnit Convert(ICompilation compilation, SyntaxTree syntaxTree, CSharpUnresolvedFile unresolvedFile)
  		{
  			if (syntaxTree == null)
  				throw new ArgumentNullException("syntaxTree");
  			if (compilation == null)
  				throw new ArgumentNullException("compilation");
  			
  			CSharpAstResolver resolver = new CSharpAstResolver(compilation, syntaxTree, unresolvedFile);
  			return (CodeCompileUnit)Convert(syntaxTree, resolver);
  		}
  		
  		/// <summary>
  		/// Converts a C# AST node to CodeDom.
  		/// </summary>
  		/// <param name="node">The input node.</param>
  		/// <param name="resolver">The AST resolver.</param>
  		/// <returns>The node converted into CodeDom</returns>
  		/// <remarks>
  		/// This conversion process requires a resolver because it needs to distinguish field/property/event references etc.
  		/// </remarks>
  		public CodeObject Convert(AstNode node, CSharpAstResolver resolver)
  		{
  			if (node == null)
  				throw new ArgumentNullException("node");
  			if (resolver == null)
  				throw new ArgumentNullException("resolver");
  			try {
  				this.resolver = resolver;
  				return node.AcceptVisitor(this);
  			} finally {
  				this.resolver = null;
  			}
  		}
  		
  		ResolveResult Resolve(AstNode node)
  		{
  			if (resolver == null)
  				return ErrorResolveResult.UnknownError;
  			else
  				return resolver.Resolve(node);
  		}
  		
  		CodeExpression Convert(Expression expr)
  		{
  			return (CodeExpression)expr.AcceptVisitor(this);
  		}
  		
  		CodeExpression[] Convert(IEnumerable<Expression> expressions)
  		{
  			List<CodeExpression> result = new List<CodeExpression>();
  			foreach (Expression expr in expressions) {
  				CodeExpression e = Convert(expr);
  				if (e != null)
  					result.Add(e);
  			}
  			return result.ToArray();
  		}
  		
  		CodeTypeReference Convert(AstType type)
  		{
  			return (CodeTypeReference)type.AcceptVisitor(this);
  		}
  		
  		CodeTypeReference[] Convert(IEnumerable<AstType> types)
  		{
  			List<CodeTypeReference> result = new List<CodeTypeReference>();
  			foreach (AstType type in types) {
  				CodeTypeReference e = Convert(type);
  				if (e != null)
  					result.Add(e);
  			}
  			return result.ToArray();
  		}
  		
  		public CodeTypeReference Convert(IType type)
  		{
  			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)
  		{
  			return (CodeStatement)stmt.AcceptVisitor(this);
  		}
  		
  		CodeStatement[] ConvertBlock(BlockStatement block)
  		{
  			List<CodeStatement> result = new List<CodeStatement>();
  			foreach (Statement stmt in block.Statements) {
  				if (stmt is EmptyStatement)
  					continue;
  				CodeStatement s = Convert(stmt);
  				if (s != null)
  					result.Add(s);
  			}
  			return result.ToArray();
  		}
  		
  		CodeStatement[] ConvertEmbeddedStatement(Statement embeddedStatement)
  		{
  			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)
  				return new CodeStatement[] { s };
  			else
  				return new CodeStatement[0];
  		}
  		
  		string MakeSnippet(AstNode node)
  		{
  			if (!AllowSnippetNodes)
  				throw new NotSupportedException();
  			StringWriter w = new StringWriter();
  			CSharpOutputVisitor v = new CSharpOutputVisitor(w, FormattingOptionsFactory.CreateMono ());
  			node.AcceptVisitor(v);
  			return w.ToString();
  		}
  		
  		/// <summary>
  		/// Converts an expression by storing it as C# snippet.
  		/// This is used for expressions that cannot be represented in CodeDom.
  		/// </summary>
  		CodeSnippetExpression MakeSnippetExpression(Expression expr)
  		{
  			return new CodeSnippetExpression(MakeSnippet(expr));
  		}
  		
  		CodeSnippetStatement MakeSnippetStatement(Statement stmt)
  		{
  			return new CodeSnippetStatement(MakeSnippet(stmt));
  		}
  
  		CodeObject IAstVisitor<CodeObject>.VisitNullNode(AstNode nullNode)
  		{
  			return null;
  		}
  
  		CodeObject IAstVisitor<CodeObject>.VisitErrorNode(AstNode errorNode)
  		{
  			return null;
  		}
  
  		CodeObject IAstVisitor<CodeObject>.VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression)
  		{
  			return MakeSnippetExpression(anonymousMethodExpression);
  		}
  		
  		CodeObject IAstVisitor<CodeObject>.VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression)
  		{
  			return MakeSnippetExpression(undocumentedExpression);
  		}
  		
  		CodeObject IAstVisitor<CodeObject>.VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression)
  		{
  			CodeArrayCreateExpression ace = new CodeArrayCreateExpression();
  			int dimensions = arrayCreateExpression.Arguments.Count;
  			int nestingDepth = arrayCreateExpression.AdditionalArraySpecifiers.Count;
  			if (dimensions > 0)
  				nestingDepth++;
  			if (nestingDepth > 1 || dimensions > 1) {
  				// CodeDom does not support jagged or multi-dimensional arrays
  				return MakeSnippetExpression(arrayCreateExpression);
  			}
  			if (arrayCreateExpression.Type.IsNull) {
  				ace.CreateType = Convert(Resolve(arrayCreateExpression).Type);
  			} else {
  				ace.CreateType = Convert(arrayCreateExpression.Type);
  			}
  			if (arrayCreateExpression.Arguments.Count == 1) {
  				ace.SizeExpression = Convert(arrayCreateExpression.Arguments.Single());
  			}
  			ace.Initializers.AddRange(Convert(arrayCreateExpression.Initializer.Elements));
  			return ace;
  		}
  		
  		CodeObject IAstVisitor<CodeObject>.VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression)
  		{
  			// Array initializers should be handled by the parent node
  			return MakeSnippetExpression(arrayInitializerExpression);
  		}
  		
  		CodeObject IAstVisitor<CodeObject>.VisitAsExpression(AsExpression asExpression)
  		{
  			return MakeSnippetExpression(asExpression);
  		}
  		
  		CodeObject IAstVisitor<CodeObject>.VisitAssignmentExpression(AssignmentExpression assignmentExpression)
  		{
  			// assignments are only supported as statements, not as expressions
  			return MakeSnippetExpression(assignmentExpression);
  		}
  		
  		CodeObject IAstVisitor<CodeObject>.VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression)
  		{
  			return new CodeBaseReferenceExpression();
  		}
  		
  		CodeObject IAstVisitor<CodeObject>.VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression)
  		{
  			CodeBinaryOperatorType op;
  			switch (binaryOperatorExpression.Operator) {
  				case BinaryOperatorType.BitwiseAnd:
  					op = CodeBinaryOperatorType.BitwiseAnd;
  					break;
  				case BinaryOperatorType.BitwiseOr:
  					op = CodeBinaryOperatorType.BitwiseOr;
  					break;
  				case BinaryOperatorType.ConditionalAnd:
  					op = CodeBinaryOperatorType.BooleanAnd;
  					break;
  				case BinaryOperatorType.ConditionalOr:
  					op = CodeBinaryOperatorType.BooleanOr;
  					break;
  				case BinaryOperatorType.GreaterThan:
  					op = CodeBinaryOperatorType.GreaterThan;
  					break;
  				case BinaryOperatorType.GreaterThanOrEqual:
  					op = CodeBinaryOperatorType.GreaterThanOrEqual;
  					break;
  				case BinaryOperatorType.LessThan:
  					op = CodeBinaryOperatorType.LessThan;
  					break;
  				case BinaryOperatorType.LessThanOrEqual:
  					op = CodeBinaryOperatorType.LessThanOrEqual;
  					break;
  				case BinaryOperatorType.Add:
  					op = CodeBinaryOperatorType.Add;
  					break;
  				case BinaryOperatorType.Subtract:
  					op = CodeBinaryOperatorType.Subtract;
  					break;
  				case BinaryOperatorType.Multiply:
  					op = CodeBinaryOperatorType.Multiply;
  					break;
  				case BinaryOperatorType.Divide:
  					op = CodeBinaryOperatorType.Divide;
  					break;
  				case BinaryOperatorType.Modulus:
  					op = CodeBinaryOperatorType.Modulus;
  					break;
  				case BinaryOperatorType.Equality:
  				case BinaryOperatorType.InEquality:
  					OperatorResolveResult rr = Resolve(binaryOperatorExpression) as OperatorResolveResult;
  					if (rr != null && rr.GetChildResults().Any(cr => cr.Type.IsReferenceType == true)) {
  						if (binaryOperatorExpression.Operator == BinaryOperatorType.Equality)
  							op = CodeBinaryOperatorType.IdentityEquality;
  						else
  							op = CodeBinaryOperatorType.IdentityInequality;
  					} else {
  						if (binaryOperatorExpression.Operator == BinaryOperatorType.Equality) {
  							op = CodeBinaryOperatorType.ValueEquality;
  						} else {
  							// CodeDom is retarded and does not support ValueInequality, so we'll simulate it using
  							// ValueEquality and Not... but CodeDom doesn't have Not either, so we use
  							// '(a == b) == false'
  							return new CodeBinaryOperatorExpression(
  								new CodeBinaryOperatorExpression(
  									Convert(binaryOperatorExpression.Left),
  									CodeBinaryOperatorType.ValueEquality,
  									Convert(binaryOperatorExpression.Right)
  								),
  								CodeBinaryOperatorType.ValueEquality,
  								new CodePrimitiveExpression(false)
  							);
  						}
  					}
  					break;
  				default:
  					// not supported: xor, shift, null coalescing
  					return MakeSnippetExpression(binaryOperatorExpression);
  			}
  			return new CodeBinaryOperatorExpression(Convert(binaryOperatorExpression.Left), op, Convert(binaryOperatorExpression.Right));
  		}
  		
  		CodeObject IAstVisitor<CodeObject>.VisitCastExpression(CastExpression castExpression)
  		{
  			return new CodeCastExpression(Convert(castExpression.Type), Convert(castExpression.Expression));
  		}
  		
  		CodeObject IAstVisitor<CodeObject>.VisitCheckedExpression(CheckedExpression checkedExpression)
  		{
  			return MakeSnippetExpression(checkedExpression);
  		}
  		
  		CodeObject IAstVisitor<CodeObject>.VisitConditionalExpression(ConditionalExpression conditionalExpression)
  		{
  			return MakeSnippetExpression(conditionalExpression);
  		}
  		
  		CodeObject IAstVisitor<CodeObject>.VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression)
  		{
  			return new CodeDefaultValueExpression(Convert(defaultValueExpression.Type));
  		}
  		
  		CodeObject IAstVisitor<CodeObject>.VisitDirectionExpression(DirectionExpression directionExpression)
  		{
  			System.CodeDom.FieldDirection direction;
  			if (directionExpression.FieldDirection == FieldDirection.Out) {
  				direction = System.CodeDom.FieldDirection.Out;
  			} else {
  				direction = System.CodeDom.FieldDirection.Ref;
  			}
  			return new CodeDirectionExpression(direction, Convert(directionExpression.Expression));
  		}
  		
  		CodeObject IAstVisitor<CodeObject>.VisitIdentifierExpression(IdentifierExpression identifierExpression)
  		{
  			ResolveResult rr = Resolve(identifierExpression);
  			LocalResolveResult lrr = rr as LocalResolveResult;
  			if (lrr != null && lrr.IsParameter) {
  				if (lrr.Variable.Name == "value" && identifierExpression.Ancestors.Any(a => a is Accessor)) {
  					return new CodePropertySetValueReferenceExpression();
  				} else {
  					return new CodeArgumentReferenceExpression(lrr.Variable.Name);
  				}
  			}
  			MemberResolveResult mrr = rr as MemberResolveResult;
  			if (mrr != null) {
  				return HandleMemberReference(null, identifierExpression.Identifier, identifierExpression.TypeArguments, mrr);
  			}
  			TypeResolveResult trr = rr as TypeResolveResult;
  			if (trr != null) {
  				CodeTypeReference typeRef;
  				if (UseFullyQualifiedTypeNames) {
  					typeRef = Convert(trr.Type);
  				} else {
  					typeRef = new CodeTypeReference(identifierExpression.Identifier);
  					typeRef.TypeArguments.AddRange(Convert(identifierExpression.TypeArguments));
  				}
  				return new CodeTypeReferenceExpression(typeRef);
  			}
  			MethodGroupResolveResult mgrr = rr as MethodGroupResolveResult;
  			if (mgrr != null || identifierExpression.TypeArguments.Any()) {
  				return new CodeMethodReferenceExpression(new CodeThisReferenceExpression(), identifierExpression.Identifier, Convert(identifierExpression.TypeArguments));
  			}
  			return new CodeVariableReferenceExpression(identifierExpression.Identifier);
  		}
  		
  		CodeObject IAstVisitor<CodeObject>.VisitIndexerExpression(IndexerExpression indexerExpression)
  		{
  			if (Resolve(indexerExpression) is ArrayAccessResolveResult)
  				return new CodeArrayIndexerExpression(Convert(indexerExpression.Target), Convert(indexerExpression.Arguments));
  			else
  				return new CodeIndexerExpression(Convert(indexerExpression.Target), Convert(indexerExpression.Arguments));
  		}
  		
  		CodeObject IAstVisitor<CodeObject>.VisitInvocationExpression(InvocationExpression invocationExpression)
  		{
  			MemberResolveResult rr = Resolve(invocationExpression) as MemberResolveResult;
  			CSharpInvocationResolveResult csRR = rr as CSharpInvocationResolveResult;
  			if (csRR != null && csRR.IsDelegateInvocation) {
  				return new CodeDelegateInvokeExpression(Convert(invocationExpression.Target), Convert(invocationExpression.Arguments));
  			}
  			
  			Expression methodExpr = invocationExpression.Target;
  			while (methodExpr is ParenthesizedExpression)
  				methodExpr = ((ParenthesizedExpression)methodExpr).Expression;
  			CodeMethodReferenceExpression mr = null;
  			MemberReferenceExpression mre = methodExpr as MemberReferenceExpression;
  			if (mre != null) {
  				mr = new CodeMethodReferenceExpression(Convert(mre.Target), mre.MemberName, Convert(mre.TypeArguments));
  			}
  			IdentifierExpression id = methodExpr as IdentifierExpression;
  			if (id != null) {
  				CodeExpression target;
  				if (rr != null && rr.Member.IsStatic)
  					target = new CodeTypeReferenceExpression(Convert(rr.Member.DeclaringType ?? SpecialType.UnknownType));
  				else
  					target = new CodeThisReferenceExpression();
  				
  				mr = new CodeMethodReferenceExpression(target, id.Identifier, Convert(id.TypeArguments));
  			}
  			if (mr != null)
  				return new CodeMethodInvokeExpression(mr, Convert(invocationExpression.Arguments));
  			else
  				return MakeSnippetExpression(invocationExpression);
  		}
  		
  		CodeObject IAstVisitor<CodeObject>.VisitIsExpression(IsExpression isExpression)
  		{
  			return MakeSnippetExpression(isExpression);
  		}
  		
  		CodeObject IAstVisitor<CodeObject>.VisitLambdaExpression(LambdaExpression lambdaExpression)
  		{
  			return MakeSnippetExpression(lambdaExpression);
  		}
  		
  		CodeObject IAstVisitor<CodeObject>.VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression)
  		{
  			CodeExpression target = Convert(memberReferenceExpression.Target);
  			ResolveResult rr = Resolve(memberReferenceExpression);
  			MemberResolveResult mrr = rr as MemberResolveResult;
  			TypeResolveResult trr = rr as TypeResolveResult;
  			if (mrr != null) {
  				return HandleMemberReference(target, memberReferenceExpression.MemberName, memberReferenceExpression.TypeArguments, mrr);
  			} else if (trr != null) {
  				return new CodeTypeReferenceExpression(Convert(trr.Type));
  			} else {
  				if (memberReferenceExpression.TypeArguments.Any() || rr is MethodGroupResolveResult) {
  					return new CodeMethodReferenceExpression(target, memberReferenceExpression.MemberName, Convert(memberReferenceExpression.TypeArguments));
  				} else {
  					return new CodePropertyReferenceExpression(target, memberReferenceExpression.MemberName);
  				}
  			}
  		}
  		
  		CodeExpression HandleMemberReference(CodeExpression target, string identifier, AstNodeCollection<AstType> typeArguments, MemberResolveResult mrr)
  		{
  			if (target == null) {
  				if (mrr.Member.IsStatic)
  					target = new CodeTypeReferenceExpression(Convert(mrr.Member.DeclaringType ?? SpecialType.UnknownType));
  				else
  					target = new CodeThisReferenceExpression();
  			}
  			if (mrr.Member is IField) {
  
gt: 				return new CodeFieldReferenceExpression(target, identifier);