diff --git a/tests/Makefile.am b/tests/Makefile.am index d0062f879ae856d0a039e4c5b91031a2edbc5db3..518a22f4369d0495f04437ad33c929f1b5e2cfaa 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -451,6 +451,11 @@ TESTS = \ objects/class-destroysinstance.vala \ objects/class-inner-types.vala \ objects/class-new-no-override.vala \ + objects/class-partial.vala \ + objects/class-partial-conflict-abstract.test \ + objects/class-partial-conflict-access.test \ + objects/class-partial-conflict-partial.test \ + objects/class-partial-conflict-sealed.test \ objects/class-vfunc-base-access.vala \ objects/classes.vala \ objects/classes-interfaces.vala \ diff --git a/tests/objects/class-partial-conflict-abstract.test b/tests/objects/class-partial-conflict-abstract.test new file mode 100644 index 0000000000000000000000000000000000000000..c34126d157f82ef49d78868f61fd989b3434a061 --- /dev/null +++ b/tests/objects/class-partial-conflict-abstract.test @@ -0,0 +1,10 @@ +Invalid Code + +partial abstract class Foo { +} + +partial class Foo { +} + +void main () { +} diff --git a/tests/objects/class-partial-conflict-access.test b/tests/objects/class-partial-conflict-access.test new file mode 100644 index 0000000000000000000000000000000000000000..d52e975324b4b2a23892087b2b77cff9312f9c71 --- /dev/null +++ b/tests/objects/class-partial-conflict-access.test @@ -0,0 +1,10 @@ +Invalid Code + +public partial class Foo { +} + +partial class Foo { +} + +void main () { +} diff --git a/tests/objects/class-partial-conflict-partial.test b/tests/objects/class-partial-conflict-partial.test new file mode 100644 index 0000000000000000000000000000000000000000..6ae9843c1e867d8a745f326014bd1798824b91a8 --- /dev/null +++ b/tests/objects/class-partial-conflict-partial.test @@ -0,0 +1,10 @@ +Invalid Code + +class Foo { +} + +partial class Foo { +} + +void main () { +} diff --git a/tests/objects/class-partial-conflict-sealed.test b/tests/objects/class-partial-conflict-sealed.test new file mode 100644 index 0000000000000000000000000000000000000000..16f7e80839616d5e4b67afd3f100117cf042fd7c --- /dev/null +++ b/tests/objects/class-partial-conflict-sealed.test @@ -0,0 +1,10 @@ +Invalid Code + +partial sealed class Foo { +} + +partial class Foo { +} + +void main () { +} diff --git a/tests/objects/class-partial.vala b/tests/objects/class-partial.vala new file mode 100644 index 0000000000000000000000000000000000000000..15477f01c44c13e85632c97765bf7cbd21da266b --- /dev/null +++ b/tests/objects/class-partial.vala @@ -0,0 +1,70 @@ +public interface IFoo { + public abstract void i1 (); +} + +public interface IBar { + public abstract void i2 (); +} + +public partial class Foo : Object { + public string p0 { get; set; } + public string f0; + public void m0 () { + } + public virtual void v0 () { + } + public virtual signal void s0 () { + } +} + +public partial class Foo : IFoo { + public string p1 { get; set; } + public string f1; + public void m1 () { + } + public virtual void v1 () { + } + public virtual signal void s1 () { + } + public void i1 () { + } +} + +public partial class Foo : IBar { + public string p2 { get; set; } + public string f2; + public void m2 () { + } + public virtual void v2 () { + } + public virtual signal void s2 () { + } + public void i2 () { + } +} + +void main () { + var foo = new Foo (); + foo.p0 = "p0"; + foo.f0 = "f0"; + foo.m0 (); + foo.v0 (); + foo.s0 (); + + foo.p1 = "p1"; + foo.f1 = "f1"; + foo.m1 (); + foo.v1 (); + foo.s1 (); + + foo.p2 = "p2"; + foo.f2 = "f2"; + foo.m2 (); + foo.v2 (); + foo.s2 (); + + assert (foo is IFoo); + foo.i1 (); + assert (foo is IBar); + foo.i2 (); +} diff --git a/vala/valaclass.vala b/vala/valaclass.vala index ee4ed624fc51c9a9709bc1441e6f5db1166bc367..a6f5ae6339679a4faf8551fc7919da7412e5de89 100644 --- a/vala/valaclass.vala +++ b/vala/valaclass.vala @@ -37,6 +37,8 @@ public class Vala.Class : ObjectTypeSymbol { */ public bool is_abstract { get; set; } + public bool is_partial { get; set; } + /** * Specifies whether this class is sealed. Sealed classes may not be * sub-classed. diff --git a/vala/valaparser.vala b/vala/valaparser.vala index f71f6e89cb4f1974ae128d769de738d01e12a206..a2b99f9ed77b326ae128d3930df95b4471f5367f 100644 --- a/vala/valaparser.vala +++ b/vala/valaparser.vala @@ -61,7 +61,8 @@ public class Vala.Parser : CodeVisitor { STATIC, VIRTUAL, ASYNC, - SEALED + SEALED, + PARTIAL } public Parser () { @@ -253,6 +254,7 @@ public class Vala.Parser : CodeVisitor { case TokenType.OVERRIDE: case TokenType.OWNED: case TokenType.PARAMS: + case TokenType.PARTIAL: case TokenType.PRIVATE: case TokenType.PROTECTED: case TokenType.PUBLIC: @@ -2733,6 +2735,7 @@ public class Vala.Parser : CodeVisitor { case TokenType.NAMESPACE: case TokenType.NEW: case TokenType.OVERRIDE: + case TokenType.PARTIAL: case TokenType.PRIVATE: case TokenType.PROTECTED: case TokenType.PUBLIC: @@ -2842,9 +2845,42 @@ public class Vala.Parser : CodeVisitor { if (ModifierFlags.SEALED in flags) { cl.is_sealed = true; } + if (ModifierFlags.PARTIAL in flags) { + if (!context.experimental) { + Report.warning (cl.source_reference, "`partial' classes are experimental"); + } + cl.is_partial = true; + } if (ModifierFlags.EXTERN in flags) { cl.is_extern = true; } + + var old_cl = parent.scope.lookup (cl.name) as Class; + if (old_cl != null && old_cl.is_partial) { + if (cl.is_partial != old_cl.is_partial) { + Report.error (cl.source_reference, "conflicting partial and not partial declarations of `%s'".printf (cl.name)); + cl.error = true; + } + if (cl.access != old_cl.access) { + Report.error (cl.source_reference, "partial declarations of `%s' have conflicting accessiblity modifiers".printf (cl.name)); + cl.error = true; + } + if (cl.is_abstract != old_cl.is_abstract) { + Report.error (cl.source_reference, "partial declarations of `%s' have conflicting abstract modifiers".printf (cl.name)); + cl.error = true; + } + if (cl.is_sealed != old_cl.is_sealed) { + Report.error (cl.source_reference, "partial declarations of `%s' have conflicting sealed modifiers".printf (cl.name)); + cl.error = true; + } + if (cl.error) { + Report.notice (old_cl.source_reference, "previous declaration of `%s' was here", old_cl.name); + return; + } + + cl = old_cl; + } + set_attributes (cl, attrs); foreach (TypeParameter type_param in type_param_list) { cl.add_type_parameter (type_param); @@ -2855,6 +2891,10 @@ public class Vala.Parser : CodeVisitor { parse_declarations (cl); + if (old_cl != null && old_cl.is_partial) { + return; + } + // ensure there is always a default construction method if (scanner.source_file.file_type == SourceFileType.SOURCE && cl.default_construction_method == null) { @@ -3530,6 +3570,10 @@ public class Vala.Parser : CodeVisitor { next (); flags |= ModifierFlags.EXTERN; break; + case TokenType.PARTIAL: + next (); + flags |= ModifierFlags.PARTIAL; + break; case TokenType.SEALED: next (); flags |= ModifierFlags.SEALED; @@ -3872,6 +3916,7 @@ public class Vala.Parser : CodeVisitor { case TokenType.NAMESPACE: case TokenType.NEW: case TokenType.OVERRIDE: + case TokenType.PARTIAL: case TokenType.PRIVATE: case TokenType.PROTECTED: case TokenType.PUBLIC: diff --git a/vala/valascanner.vala b/vala/valascanner.vala index 36f274e6fc9cbd36d8771ba64b9b173e3902bbed..daeb97febe3a4c65e72297f3ee95effa3bafaf17 100644 --- a/vala/valascanner.vala +++ b/vala/valascanner.vala @@ -533,7 +533,14 @@ public class Vala.Scanner { } break; case 'p': - if (matches (begin, "private")) return TokenType.PRIVATE; + switch (begin[1]) { + case 'r': + if (matches (begin, "private")) return TokenType.PRIVATE; + break; + case 'a': + if (matches (begin, "partial")) return TokenType.PARTIAL; + break; + } break; case 'u': if (matches (begin, "unowned")) return TokenType.UNOWNED; diff --git a/vala/valatokentype.vala b/vala/valatokentype.vala index 9cc6d1c746480611b6009890070105984256b762..2c64ec1b00c618715858303191e8991a835afb46 100644 --- a/vala/valatokentype.vala +++ b/vala/valatokentype.vala @@ -115,6 +115,7 @@ public enum Vala.TokenType { OVERRIDE, OWNED, PARAMS, + PARTIAL, PERCENT, PLUS, PRIVATE, @@ -249,6 +250,7 @@ public enum Vala.TokenType { case OVERRIDE: return "`override'"; case OWNED: return "`owned'"; case PARAMS: return "`params'"; + case PARTIAL: return "`partial'"; case PERCENT: return "`%'"; case PLUS: return "`+'"; case PRIVATE: return "`private'";