[Vala] [PATCH] Implement alias functionality for 'using' directive



Required to disambiguate symbol references in case of matches from
multiple 'using' statements.
---
 tests/namespaces/aliases.vala |   30 ++++++++++++
 vala/valacodewriter.vala      |    6 ++-
 vala/valamemberaccess.vala    |   26 +++++++----
 vala/valaparser.vala          |   17 ++++++-
 vala/valasymbolresolver.vala  |   51 ++++++++++++-------
 vala/valausingdirective.vala  |  105 ++++++++++++++++++++++++++++++++++++++--
 6 files changed, 197 insertions(+), 38 deletions(-)
 create mode 100644 tests/namespaces/aliases.vala

diff --git a/tests/namespaces/aliases.vala b/tests/namespaces/aliases.vala
new file mode 100644
index 0000000..277ae6e
--- /dev/null
+++ b/tests/namespaces/aliases.vala
@@ -0,0 +1,30 @@
+// Based on example of ambuiguity resolution from C# language
+// reference 9.3.2
+
+namespace N1 {
+       public class A {
+               public A() {}
+               public int test() { return 1234; }
+       }
+}
+namespace N2 {
+       public class A {
+               public A() {}
+               public int test() { return 5678; }
+       }
+}
+
+namespace N3 {
+       using N1;
+       using N2;
+       using A = N1.A;
+       public class B : A {                              // A means N1.A
+               public B() { base(); }
+       }
+}
+
+public static void main() {
+       assert(new N1.A().test() == 1234);
+       assert(new N2.A().test() == 5678);
+       assert(new N3.B().test() == 1234);
+}
diff --git a/vala/valacodewriter.vala b/vala/valacodewriter.vala
index b6e31c2..8cecbe8 100644
--- a/vala/valacodewriter.vala
+++ b/vala/valacodewriter.vala
@@ -124,7 +124,11 @@ public class Vala.CodeWriter : CodeVisitor {
 
        public override void visit_using_directive (UsingDirective ns) {
                if (type == CodeWriterType.FAST) {
-                       write_string ("using %s;\n".printf (ns.namespace_symbol.name));
+                       if (ns.alias != null) {
+                               write_string ("using %s = %s;\n".printf (ns.alias, ns.symbol.name));
+                       } else {
+                               write_string ("using %s;\n".printf (ns.symbol.name));
+                       }
                }
        }
 
diff --git a/vala/valamemberaccess.vala b/vala/valamemberaccess.vala
index dfb0eb6..9026bb3 100644
--- a/vala/valamemberaccess.vala
+++ b/vala/valamemberaccess.vala
@@ -277,17 +277,25 @@ public class Vala.MemberAccess : Expression {
                        }
 
                        if (symbol_reference == null && source_reference != null) {
-                               foreach (UsingDirective ns in source_reference.using_directives) {
-                                       var local_sym = ns.namespace_symbol.scope.lookup (member_name);
-                                       if (local_sym != null) {
-                                               if (symbol_reference != null && symbol_reference != 
local_sym) {
-                                                       error = true;
-                                                       Report.error (source_reference, "`%s' is an ambiguous 
reference between `%s' and `%s'".printf (member_name, symbol_reference.get_full_name (), 
local_sym.get_full_name ()));
-                                                       return false;
-                                               }
-                                               symbol_reference = local_sym;
+                               var scanner = new UsingDirective.Scanner (member_name, false);
+                               foreach (UsingDirective ud in source_reference.using_directives) {
+                                       scanner.try_match (ud);
+                               }
+                               if (scanner.ambiguous) {
+                                       // Rescan to pick up errors
+                                       scanner = new UsingDirective.Scanner (member_name, true);
+                                       foreach (UsingDirective ud in source_reference.using_directives) {
+                                               scanner.try_match (ud);
+                                       }
+                                       if (scanner.alias_match) {
+                                               Report.error (source_reference, "`%s' is an ambiguous 
reference to duplicate aliases:%s".printf (member_name, scanner.error_string.str));
+                                       } else {
+                                               Report.error (source_reference, "`%s' is an ambiguous 
reference; add one of these aliases to resolve:%s".printf (member_name, scanner.error_string.str));
                                        }
+                                       error = true;
+                                       return false;
                                }
+                               symbol_reference = scanner.match;
                        }
                } else {
                        if (inner.error) {
diff --git a/vala/valaparser.vala b/vala/valaparser.vala
index 136bf01..f042bb9 100644
--- a/vala/valaparser.vala
+++ b/vala/valaparser.vala
@@ -2383,9 +2383,20 @@ public class Vala.Parser : CodeVisitor {
                        do {
                                var begin = get_location ();
                                var sym = parse_symbol_name ();
-                               var ns_ref = new UsingDirective (sym, get_src (begin));
-                               scanner.source_file.add_using_directive (ns_ref);
-                               ns.add_using_directive (ns_ref);
+                               if (accept (TokenType.ASSIGN)) {
+                                       var alias_sym = parse_symbol_name ();
+                                       if (sym.inner != null) {
+                                               Report.error (get_src (begin), "alias name must be a simple 
identifier");
+                                       } else {
+                                               var ud = new UsingDirective.with_alias (sym.name, alias_sym, 
get_src (begin));
+                                               scanner.source_file.add_using_directive (ud);
+                                               ns.add_using_directive (ud);
+                                       }
+                               } else {
+                                       var ns_ref = new UsingDirective (sym, get_src (begin));
+                                       scanner.source_file.add_using_directive (ns_ref);
+                                       ns.add_using_directive (ns_ref);
+                               }
                        } while (accept (TokenType.COMMA));
                        expect (TokenType.SEMICOLON);
                }
diff --git a/vala/valasymbolresolver.vala b/vala/valasymbolresolver.vala
index 3f874fa..69a84bb 100644
--- a/vala/valasymbolresolver.vala
+++ b/vala/valasymbolresolver.vala
@@ -215,12 +215,18 @@ public class Vala.SymbolResolver : CodeVisitor {
        }
 
        public override void visit_using_directive (UsingDirective ns) {
-               var unresolved_symbol = ns.namespace_symbol as UnresolvedSymbol;
+               var unresolved_symbol = ns.symbol as UnresolvedSymbol;
                if (unresolved_symbol != null) {
-                       ns.namespace_symbol = resolve_symbol (unresolved_symbol);
-                       if (!(ns.namespace_symbol is Namespace)) {
+                       ns.symbol = resolve_symbol (unresolved_symbol);
+                       if (ns.alias == null) {
+                               if (!(ns.symbol is Namespace)) {
+                                       ns.error = true;
+                                       Report.error (ns.source_reference, "The namespace name `%s' could not 
be found".printf (unresolved_symbol.to_string ()));
+                                       return;
+                               }
+                       } else if (ns.symbol == null) {
                                ns.error = true;
-                               Report.error (ns.source_reference, "The namespace name `%s' could not be 
found".printf (unresolved_symbol.to_string ()));
+                               Report.error (ns.source_reference, "The symbol `%s' could not be 
found".printf (unresolved_symbol.to_string ()));
                                return;
                        }
                }
@@ -243,28 +249,35 @@ public class Vala.SymbolResolver : CodeVisitor {
 
                                scope = scope.parent_scope;
                        }
+
                        if (sym == null && unresolved_symbol.source_reference != null) {
+                               var scanner = new UsingDirective.Scanner(unresolved_symbol.name, false);
                                foreach (UsingDirective ns in 
unresolved_symbol.source_reference.using_directives) {
-                                       if (ns.error || ns.namespace_symbol is UnresolvedSymbol) {
+                                       Symbol local_sym = ns.symbol;
+                                       if (ns.error || !(local_sym is Namespace || local_sym is TypeSymbol 
|| local_sym is TypeParameter)) {
                                                continue;
                                        }
-
-                                       var local_sym = ns.namespace_symbol.scope.lookup 
(unresolved_symbol.name);
-
-                                       // only look for types and type containers
-                                       if (!(local_sym is Namespace || local_sym is TypeSymbol || sym is 
TypeParameter)) {
-                                               local_sym = null;
-                                       }
-
-                                       if (local_sym != null) {
-                                               if (sym != null && sym != local_sym) {
-                                                       unresolved_symbol.error = true;
-                                                       Report.error (unresolved_symbol.source_reference, 
"`%s' is an ambiguous reference between `%s' and `%s'".printf (unresolved_symbol.name, sym.get_full_name (), 
local_sym.get_full_name ()));
-                                                       return null;
+                                       scanner.try_match (ns);
+                               }
+                               if (scanner.ambiguous) {
+                                       // Rescan to build up error string
+                                       scanner = new UsingDirective.Scanner(unresolved_symbol.name, true);
+                                       foreach (UsingDirective ns in 
unresolved_symbol.source_reference.using_directives) {
+                                               Symbol local_sym = ns.symbol;
+                                               if (ns.error || !(local_sym is Namespace || local_sym is 
TypeSymbol || local_sym is TypeParameter)) {
+                                                       continue;
                                                }
-                                               sym = local_sym;
+                                               scanner.try_match (ns);
+                                       }
+                                       if (scanner.alias_match) {
+                                               Report.error (unresolved_symbol.source_reference, "`%s' is an 
ambiguous reference to duplicate aliases:%s".printf (unresolved_symbol.name, scanner.error_string.str));
+                                       } else {
+                                               Report.error (unresolved_symbol.source_reference, "`%s' is an 
ambiguous reference; add one of these aliases to resolve:%s".printf (unresolved_symbol.name, 
scanner.error_string.str));
                                        }
+                                       unresolved_symbol.error = true;
+                                       return null;
                                }
+                               sym = scanner.match;
                        }
                        return sym;
                } else {
diff --git a/vala/valausingdirective.vala b/vala/valausingdirective.vala
index 9fb0766..9260cbc 100644
--- a/vala/valausingdirective.vala
+++ b/vala/valausingdirective.vala
@@ -27,22 +27,115 @@ using GLib;
  */
 public class Vala.UsingDirective : CodeNode {
        /**
-        * The symbol of the namespace this using directive is referring to.
+        * The symbol that this using directive is referring to, either a
+        * namespace (alias == null) or the aliased symbol (alias != null).
         */
-       public Symbol namespace_symbol { get; set; }
+       public Symbol symbol { get; set; }
 
        /**
-        * Creates a new using directive.
+        * Alias name, for alias using directives, or null if this is a
+        * namespace using directive.
+        */
+       public string? alias { get; set; }
+
+       /**
+        * Lookup a symbol according to this directive.
+        * @param name Name to lookup
+        * @return Symbol, or null if not found
+        */
+       public Symbol? lookup (string name) {
+               if (alias == null) 
+                       return symbol.scope.lookup (name);
+               if (name == alias)
+                       return symbol;
+               return null;
+       }
+
+       /**
+        * Creates a new namespace using directive.
+        *
+        * @param symbol namespace symbol
+        * @return                 newly created using directive
+        */
+       public UsingDirective (Symbol symbol, SourceReference? source_reference = null) {
+               this.symbol = symbol;
+               this.source_reference = source_reference;
+       }
+
+       /**
+        * Creates a new alias using directive.
         *
-        * @param namespace_symbol namespace symbol
+        * @param alias alias name
+        * @param symbol symbol referenced by alias
         * @return                 newly created using directive
         */
-       public UsingDirective (Symbol namespace_symbol, SourceReference? source_reference = null) {
-               this.namespace_symbol = namespace_symbol;
+       public UsingDirective.with_alias (string alias, Symbol symbol, SourceReference? source_reference = 
null) {
+               this.alias = alias;
+               this.symbol = symbol;
                this.source_reference = source_reference;
        }
        
        public override void accept (CodeVisitor visitor) {
                visitor.visit_using_directive (this);
        }
+
+       /**
+        * Place to accumulate results of scanning a number of
+        * UsingDirectives looking for matches.
+        */
+       public static class Scanner {
+               public string name;
+               public Symbol? match;
+               public bool alias_match;
+               public bool ambiguous;
+               public bool for_errors;
+               public StringBuilder error_string;
+
+               /**
+                * Constructor.
+                * @param name symbol name to look up
+                * @param for_errors true to accumulate messages for ambiguous matches in error_string
+                */
+               public Scanner(string name, bool for_errors) {
+                       this.name = name;
+                       this.for_errors = for_errors;
+                       if (for_errors)
+                               error_string = new StringBuilder();
+               }
+
+               /**
+                * Try a match with the given UsingDirective and accumulate
+                * results in the Scanner.  An alias match always overrides any
+                * ambiguity due to multiple namespace matches (see C# language
+                * spec 9.3.2).
+                */
+               public void try_match(UsingDirective ud) {
+                       Symbol? sym = ud.lookup(name);
+                       if (sym == null)
+                               return;
+
+                       bool ud_is_alias = ud.alias != null;
+                       if (sym == match && alias_match == ud_is_alias)
+                               return;
+
+                       // We have a new match to process
+                       if (for_errors) {
+                               if (ud_is_alias && !alias_match)
+                                       error_string = new StringBuilder();
+                               if (ud_is_alias)
+                                       error_string.append_printf("\n      %s", sym.get_full_name());
+                               else 
+                                       error_string.append_printf("\n      using %s = %s;", name, 
sym.get_full_name());
+                       }
+                       if (match == null || (ud_is_alias && !alias_match)) {
+                               match = sym;
+                               alias_match = ud_is_alias;
+                               ambiguous = false;
+                       } else if (!ud_is_alias && alias_match) {
+                               // Alias overrides using
+                       } else {
+                               ambiguous = true;
+                       }
+               }
+       }
 }
-- 
1.7.2.5




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]