>From 6c101e3de51675265e064453acf46f99b68e102c Mon Sep 17 00:00:00 2001
From: Philip Van Hoof <philip codeminded be>
Date: Mon, 14 Oct 2013 17:41:17 +0200
Subject: [PATCH] Adds support for simple extension methods

For the moment you can't use the parameter's identifier in the method,
you must use this (which isn't correct yet).
---
 tests/Makefile.am                   |  1 +
 tests/methods/extensionmethods.vala | 32 ++++++++++++++++++++++++++++++++
 vala/valamethod.vala                |  2 ++
 vala/valanamespace.vala             | 15 +++++++++++++++
 vala/valaparser.vala                | 16 +++++++++++++++-
 5 files changed, 65 insertions(+), 1 deletion(-)
 create mode 100644 tests/methods/extensionmethods.vala

diff --git a/tests/Makefile.am b/tests/Makefile.am
index de7e823..5b88123 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -57,6 +57,7 @@ TESTS = \
 	methods/bug653908.vala \
 	methods/bug663210.vala \
 	methods/generics.vala \
+	methods/extensionmethods.vala \
 	control-flow/break.vala \
 	control-flow/expressions-conditional.vala \
 	control-flow/for.vala \
diff --git a/tests/methods/extensionmethods.vala b/tests/methods/extensionmethods.vala
new file mode 100644
index 0000000..073a8de
--- /dev/null
+++ b/tests/methods/extensionmethods.vala
@@ -0,0 +1,32 @@
+
+public class TestB
+{
+	public void Hello() {
+		print ("Hello\n");
+	}
+}
+
+public class TestA
+{
+	public void Test(TestB b) {
+		b.Hello();
+	}
+}
+
+public void Hallo(this TestA a, string msg)
+{
+	print ("Hallo %s\n", msg);
+	TestB b = new TestB();
+	this.Test(b);
+}
+
+public class Program {
+	static void main ()
+	{
+		TestA a = new TestA();
+		TestB b = new TestB();
+		a.Test(b);
+		a.Hallo("hihi");
+
+	}
+}
diff --git a/vala/valamethod.vala b/vala/valamethod.vala
index 663ae6f..3b5df12 100644
--- a/vala/valamethod.vala
+++ b/vala/valamethod.vala
@@ -41,6 +41,8 @@ public class Vala.Method : Subroutine {
 		}
 	}
 
+	public Parameter extension_for { get; set; }
+
 	public override bool has_result {
 		get { return !(return_type is VoidType); }
 	}
diff --git a/vala/valanamespace.vala b/vala/valanamespace.vala
index 47dd4ee..d8d6770 100644
--- a/vala/valanamespace.vala
+++ b/vala/valanamespace.vala
@@ -54,6 +54,21 @@ public class Vala.Namespace : Symbol {
 		access = SymbolAccessibility.PUBLIC;
 	}
 
+	public Class? find_in_tree (string f_name) {
+		foreach (Class cl in classes) {
+			if (cl.name == f_name)
+				return cl;
+		}
+		foreach (Namespace ns in namespaces) {
+			foreach (Class cl in ns.classes) {
+				if (cl.name == f_name)
+					return cl;
+			}
+			return ns.find_in_tree (f_name);
+		}
+		return null;
+	}
+
 	/**
 	 * Adds a new using directive with the specified namespace.
 	 *
diff --git a/vala/valaparser.vala b/vala/valaparser.vala
index a7ca9c0..22e708c 100644
--- a/vala/valaparser.vala
+++ b/vala/valaparser.vala
@@ -2615,6 +2615,10 @@ public class Vala.Parser : CodeVisitor {
 		expect (TokenType.OPEN_PARENS);
 		if (current () != TokenType.CLOSE_PARENS) {
 			do {
+				if (accept (TokenType.THIS)) {
+					method.extension_for = parse_parameter ();
+					continue;
+				}
 				var param = parse_parameter ();
 				method.add_parameter (param);
 			} while (accept (TokenType.COMMA));
@@ -2641,7 +2645,16 @@ public class Vala.Parser : CodeVisitor {
 			method.external = true;
 		}
 
-		parent.add_method (method);
+		if (method.extension_for != null) {
+			var cl = context.root.find_in_tree (method.extension_for.variable_type.to_string());
+			if (cl != null) {
+				cl.add_method (method);
+			} else {
+				throw new ParseError.SYNTAX (get_error ("Could not find class `%s' to add extension method `%s' to".printf(method.extension_for.variable_type.to_string(), method.name)));
+			}
+		} else {
+			parent.add_method (method);
+		}
 	}
 
 	void parse_property_declaration (Symbol parent, List<Attribute>? attrs) throws ParseError {
@@ -3181,6 +3194,7 @@ public class Vala.Parser : CodeVisitor {
 		if (accept (TokenType.ASSIGN)) {
 			param.initializer = parse_expression ();
 		}
+
 		return param;
 	}
 
-- 
1.8.4.rc3