From 0b29102431f0d0afaa33877acd4e31d74599c991 Mon Sep 17 00:00:00 2001
From: Daniel Grunwald <daniel@danielgrunwald.de>
Date: Sat, 15 Oct 2005 20:42:25 +0000
Subject: [PATCH] BooBinding: Implemented inferring the return type from the
 "return" statement.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@578 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
---
 .../Src/CodeCompletion/ConvertVisitor.cs      |  4 ++
 .../Src/CodeCompletion/InferredReturnType.cs  | 63 ++++++++++++++++---
 .../Src/CodeCompletion/ResolveVisitor.cs      | 40 ++++++++++++
 .../Src/Dom/Implementations/NullReturnType.cs |  2 +-
 .../Project/Src/Dom/MemberLookupHelper.cs     | 14 +++++
 .../Src/Dom/NRefactoryResolver/TypeVisitor.cs |  3 +-
 6 files changed, 116 insertions(+), 10 deletions(-)

diff --git a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/ConvertVisitor.cs b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/ConvertVisitor.cs
index 72b95dc7ff..a4a8efca62 100644
--- a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/ConvertVisitor.cs
+++ b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/ConvertVisitor.cs
@@ -234,10 +234,14 @@ namespace Grunwald.BooBinding.CodeCompletion
 		// TODO: Type inference
 		IReturnType CreateReturnType(AST.Method node, IMethod method)
 		{
+			if (node.ReturnType == null)
+				return new InferredReturnType(node.Body);
 			return CreateReturnType(node.ReturnType, method);
 		}
 		IReturnType CreateReturnType(AST.Property property)
 		{
+			if (property.Type == null && property.Getter != null && property.Getter.Body != null)
+				return new InferredReturnType(property.Getter.Body);
 			return CreateReturnType(property.Type);
 		}
 		
diff --git a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/InferredReturnType.cs b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/InferredReturnType.cs
index 6907ba8853..234e0b7d9c 100644
--- a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/InferredReturnType.cs
+++ b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/InferredReturnType.cs
@@ -18,29 +18,76 @@ namespace Grunwald.BooBinding.CodeCompletion
 	public class InferredReturnType : ProxyReturnType
 	{
 		Expression expression;
+		Block block;
+		IReturnType cachedType;
 		
 		public InferredReturnType(Expression expression)
 		{
+			if (expression == null) throw new ArgumentNullException("expression");
 			this.expression = expression;
 		}
 		
-		bool needInfer = true;
-		IReturnType cachedType;
+		public InferredReturnType(Block block)
+		{
+			if (block == null) throw new ArgumentNullException("block");
+			this.block = block;
+		}
+		
+		public override bool IsDefaultReturnType {
+			get {
+				IReturnType baseType = BaseType;
+				return (baseType != null) ? baseType.IsDefaultReturnType : false;
+			}
+		}
 		
 		public override IReturnType BaseType {
 			get {
-				if (needInfer) {
-					needInfer = false;
+				// clear up references to method/expression after the type has been resolved
+				if (block != null) {
+					GetReturnTypeVisitor v = new GetReturnTypeVisitor();
+					v.Visit(block);
+					block = null;
+					if (v.noReturnStatement)
+						cachedType = ReflectionReturnType.Void;
+					else if (v.result is NullReturnType)
+						cachedType = ReflectionReturnType.Object;
+					else
+						cachedType = v.result;
+				} else if (expression != null) {
 					cachedType = new BooResolver().GetTypeOfExpression(expression);
+					expression = null;
 				}
 				return cachedType;
 			}
 		}
 		
-		public override bool IsDefaultReturnType {
-			get {
-				IReturnType baseType = BaseType;
-				return (baseType != null) ? baseType.IsDefaultReturnType : false;
+		class GetReturnTypeVisitor : DepthFirstVisitor
+		{
+			public IReturnType result;
+			public bool noReturnStatement = true;
+			
+			public override void OnReturnStatement(ReturnStatement node)
+			{
+				noReturnStatement = false;
+				if (node.Expression == null) {
+					result = ReflectionReturnType.Void;
+				} else {
+					result = new BooResolver().GetTypeOfExpression(node.Expression);
+				}
+			}
+			
+			public override void OnYieldStatement(YieldStatement node)
+			{
+				noReturnStatement = false;
+				result = ReflectionReturnType.CreatePrimitive(typeof(System.Collections.IEnumerable));
+			}
+			
+			public override bool Visit(Node node)
+			{
+				if (result != null && !(result is NullReturnType))
+					return false;
+				else
+					return base.Visit(node);
 			}
 		}
 	}
diff --git a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/ResolveVisitor.cs b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/ResolveVisitor.cs
index eaccdfd0d5..88072f58eb 100644
--- a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/ResolveVisitor.cs
+++ b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/ResolveVisitor.cs
@@ -369,6 +369,46 @@ namespace Grunwald.BooBinding.CodeCompletion
 		}
 		#endregion
 		
+		public override void OnBinaryExpression(BinaryExpression node)
+		{
+			switch (node.Operator) {
+				case BinaryOperatorType.GreaterThan:
+				case BinaryOperatorType.GreaterThanOrEqual:
+				case BinaryOperatorType.Inequality:
+				case BinaryOperatorType.LessThan:
+				case BinaryOperatorType.LessThanOrEqual:
+				case BinaryOperatorType.Match:
+				case BinaryOperatorType.Member:
+				case BinaryOperatorType.NotMatch:
+				case BinaryOperatorType.NotMember:
+				case BinaryOperatorType.Or:
+				case BinaryOperatorType.And:
+				case BinaryOperatorType.ReferenceEquality:
+				case BinaryOperatorType.ReferenceInequality:
+				case BinaryOperatorType.TypeTest:
+					MakeResult(ReflectionReturnType.Bool);
+					break;
+				default:
+					if (node.Left == null) {
+						if (node.Right == null) {
+							resolveResult = null;
+						} else {
+							node.Right.Accept(this);
+						}
+						return;
+					} else if (node.Right == null) {
+						node.Left.Accept(this);
+						return;
+					}
+					node.Left.Accept(this);
+					IReturnType left = (resolveResult != null) ? resolveResult.ResolvedType : null;
+					node.Right.Accept(this);
+					IReturnType right = (resolveResult != null) ? resolveResult.ResolvedType : null;
+					MakeResult(MemberLookupHelper.GetCommonType(left, right));
+					break;
+			}
+		}
+		
 		protected override void OnError(Node node, Exception error)
 		{
 			MessageService.ShowError(error, "ResolveVisitor: error processing " + node);
diff --git a/src/Main/Base/Project/Src/Dom/Implementations/NullReturnType.cs b/src/Main/Base/Project/Src/Dom/Implementations/NullReturnType.cs
index 35903dbd77..6b414cb813 100644
--- a/src/Main/Base/Project/Src/Dom/Implementations/NullReturnType.cs
+++ b/src/Main/Base/Project/Src/Dom/Implementations/NullReturnType.cs
@@ -11,8 +11,8 @@ using ICSharpCode.Core;
 
 namespace ICSharpCode.SharpDevelop.Dom
 {
-	/// <summary>The type of the 'null'/'nothing' literal.</summary>
 	[Serializable]
+	/// <summary>The type of the 'null'/'nothing' literal.</summary>
 	public sealed class NullReturnType : AbstractReturnType
 	{
 		public static readonly NullReturnType Instance = new NullReturnType();
diff --git a/src/Main/Base/Project/Src/Dom/MemberLookupHelper.cs b/src/Main/Base/Project/Src/Dom/MemberLookupHelper.cs
index 228646f5b1..24c8c039f2 100644
--- a/src/Main/Base/Project/Src/Dom/MemberLookupHelper.cs
+++ b/src/Main/Base/Project/Src/Dom/MemberLookupHelper.cs
@@ -671,5 +671,19 @@ namespace ICSharpCode.SharpDevelop.Dom
 			}
 		}
 		#endregion
+		
+		/// <summary>
+		/// Gets the common base type of a and b.
+		/// </summary>
+		public static IReturnType GetCommonType(IReturnType a, IReturnType b)
+		{
+			if (a == null) return b;
+			if (b == null) return a;
+			if (ConversionExists(a, b))
+				return b;
+			if (ConversionExists(b, a))
+				return a;
+			return ReflectionReturnType.Object;
+		}
 	}
 }
diff --git a/src/Main/Base/Project/Src/Dom/NRefactoryResolver/TypeVisitor.cs b/src/Main/Base/Project/Src/Dom/NRefactoryResolver/TypeVisitor.cs
index 2eea46f108..bae24230c1 100644
--- a/src/Main/Base/Project/Src/Dom/NRefactoryResolver/TypeVisitor.cs
+++ b/src/Main/Base/Project/Src/Dom/NRefactoryResolver/TypeVisitor.cs
@@ -60,7 +60,8 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
 				case BinaryOperatorType.GreaterThanOrEqual:
 					return ReflectionReturnType.Bool;
 				default:
-					return binaryOperatorExpression.Left.AcceptVisitor(this, data);
+					return MemberLookupHelper.GetCommonType(binaryOperatorExpression.Left.AcceptVisitor(this, data) as IReturnType,
+					                                        binaryOperatorExpression.Right.AcceptVisitor(this, data) as IReturnType);
 			}
 		}