2004-05-29 Martin Baulig <martin@ximian.com>
[mono.git] / mcs / mcs / namespace.cs
index e81ab2ec5be002b4fa9039e8bde75116d538c2cc..387f206cf3f0bb70494d32da9059c1a24fa27fcc 100755 (executable)
@@ -8,7 +8,6 @@
 //
 using System;
 using System.Collections;
-using Mono.Languages;
 
 namespace Mono.CSharp {
 
@@ -17,33 +16,14 @@ namespace Mono.CSharp {
        /// </summary>
        public class Namespace {
                static ArrayList all_namespaces = new ArrayList ();
+               static Hashtable namespaces_map = new Hashtable ();
                
                Namespace parent;
-               string name;
-               ArrayList using_clauses;
-               Hashtable aliases;
-               public bool DeclarationFound = false;
+               string fullname;
+               ArrayList entries;
+               Hashtable namespaces;
+               Hashtable defined_names;
 
-               //
-               // This class holds the location where a using definition is
-               // done, and whether it has been used by the program or not.
-               //
-               // We use this to flag using clauses for namespaces that do not
-               // exist.
-               //
-               public class UsingEntry {
-                       public string Name;
-                       public bool Used;
-                       public Location Location;
-                       
-                       public UsingEntry (string name, Location loc)
-                       {
-                               Name = name;
-                               Location = loc;
-                               Used = false;
-                       }
-               }
-               
                /// <summary>
                ///   Constructor Takes the current namespace and the
                ///   name.  This is bootstrapped with parent == null
@@ -51,10 +31,106 @@ namespace Mono.CSharp {
                /// </summary>
                public Namespace (Namespace parent, string name)
                {
-                       this.name = name;
                        this.parent = parent;
 
+                       string pname = parent != null ? parent.Name : "";
+                               
+                       if (pname == "")
+                               fullname = name;
+                       else
+                               fullname = parent.Name + "." + name;
+
+                       entries = new ArrayList ();
+                       namespaces = new Hashtable ();
+                       defined_names = new Hashtable ();
+
                        all_namespaces.Add (this);
+                       if (namespaces_map.Contains (fullname))
+                               return;
+                       namespaces_map [fullname] = true;
+               }
+
+               public static bool IsNamespace (string name)
+               {
+                       return namespaces_map [name] != null;
+               }
+               
+               public static Namespace Root = new Namespace (null, "");
+
+               public Namespace GetNamespace (string name, bool create)
+               {
+                       int pos = name.IndexOf ('.');
+
+                       Namespace ns;
+                       string first;
+                       if (pos >= 0)
+                               first = name.Substring (0, pos);
+                       else
+                               first = name;
+
+                       ns = (Namespace) namespaces [first];
+                       if (ns == null) {
+                               if (!create)
+                                       return null;
+
+                               ns = new Namespace (this, first);
+                               namespaces.Add (first, ns);
+                       }
+
+                       if (pos >= 0)
+                               ns = ns.GetNamespace (name.Substring (pos + 1), create);
+
+                       return ns;
+               }
+
+               public static Namespace LookupNamespace (string name, bool create)
+               {
+                       return Root.GetNamespace (name, create);
+               }
+
+               public object Lookup (DeclSpace ds, string name)
+               {
+                       object o = Lookup (name);
+
+                       Type t;
+                       DeclSpace tdecl = o as DeclSpace;
+                       if (tdecl != null) {
+                               t = tdecl.DefineType ();
+
+                               if ((ds == null) || ds.CheckAccessLevel (t))
+                                       return t;
+                       }
+
+                       Namespace ns = GetNamespace (name, false);
+                       if (ns != null)
+                               return ns;
+
+                       t = TypeManager.LookupType (DeclSpace.MakeFQN (fullname, name));
+                       if ((t == null) || ((ds != null) && !ds.CheckAccessLevel (t)))
+                               return null;
+
+                       return t;
+               }
+
+               public void AddNamespaceEntry (NamespaceEntry entry)
+               {
+                       entries.Add (entry);
+               }
+
+               public void DefineName (string name, object o)
+               {
+                       defined_names.Add (name, o);
+               }
+
+               public object Lookup (string name)
+               {
+                       return defined_names [name];
+               }
+
+               static public ArrayList UserDefinedNamespaces {
+                       get {
+                               return all_namespaces;
+                       }
                }
 
                /// <summary>
@@ -62,12 +138,7 @@ namespace Mono.CSharp {
                /// </summary>
                public string Name {
                        get {
-                               string pname = parent != null ? parent.Name : "";
-                               
-                               if (pname == "")
-                                       return name;
-                               else
-                                       return parent.Name + "." + name;
+                               return fullname;
                        }
                }
 
@@ -81,6 +152,173 @@ namespace Mono.CSharp {
                        }
                }
 
+               public static void DefineNamespaces (SymbolWriter symwriter)
+               {
+                       foreach (Namespace ns in all_namespaces) {
+                               foreach (NamespaceEntry entry in ns.entries)
+                                       entry.DefineNamespace (symwriter);
+                       }
+               }
+
+               /// <summary>
+               ///   Used to validate that all the using clauses are correct
+               ///   after we are finished parsing all the files.  
+               /// </summary>
+               public static void VerifyUsing ()
+               {
+                       foreach (Namespace ns in all_namespaces) {
+                               foreach (NamespaceEntry entry in ns.entries)
+                                       entry.VerifyUsing ();
+                       }
+               }
+
+               public override string ToString ()
+               {
+                       if (this == Root)
+                               return "Namespace (<root>)";
+                       else
+                               return String.Format ("Namespace ({0})", Name);
+               }
+       }
+
+       public class NamespaceEntry
+       {
+               Namespace ns;
+               NamespaceEntry parent, implicit_parent;
+               SourceFile file;
+               int symfile_id;
+               Hashtable aliases;
+               ArrayList using_clauses;
+               public bool DeclarationFound = false;
+
+               //
+               // This class holds the location where a using definition is
+               // done, and whether it has been used by the program or not.
+               //
+               // We use this to flag using clauses for namespaces that do not
+               // exist.
+               //
+               public class UsingEntry {
+                       public readonly string Name;
+                       public readonly NamespaceEntry NamespaceEntry;
+                       public readonly Location Location;
+                       
+                       public UsingEntry (NamespaceEntry entry, string name, Location loc)
+                       {
+                               Name = name;
+                               NamespaceEntry = entry;
+                               Location = loc;
+                       }
+
+                       Namespace resolved_ns;
+
+                       public Namespace Resolve ()
+                       {
+                               if (resolved_ns != null)
+                                       return resolved_ns;
+
+                               Namespace curr_ns = NamespaceEntry.NS;
+                               while ((curr_ns != null) && (resolved_ns == null)) {
+                                       resolved_ns = curr_ns.GetNamespace (Name, false);
+
+                                       if (resolved_ns == null)
+                                               curr_ns = curr_ns.Parent;
+                               }
+
+                               return resolved_ns;
+                       }
+               }
+
+               public class AliasEntry {
+                       public readonly string Name;
+                       public readonly Expression Alias;
+                       public readonly NamespaceEntry NamespaceEntry;
+                       public readonly Location Location;
+                       
+                       public AliasEntry (NamespaceEntry entry, string name, Expression alias, Location loc)
+                       {
+                               Name = name;
+                               Alias = alias;
+                               NamespaceEntry = entry;
+                               Location = loc;
+                       }
+
+                       object resolved;
+
+                       public object Resolve ()
+                       {
+                               if (resolved != null)
+                                       return resolved;
+
+                               NamespaceEntry curr_ns = NamespaceEntry;
+
+                               string alias = Alias.ToString ();
+                               while ((curr_ns != null) && (resolved == null)) {
+                                       resolved = curr_ns.Lookup (null, alias, false, Location);
+
+                                       if (resolved == null)
+                                               curr_ns = curr_ns.Parent;
+                               }
+
+                               return resolved;
+                       }
+               }
+
+               public NamespaceEntry (NamespaceEntry parent, SourceFile file, string name, Location loc)
+                       : this (parent, file, name, false, loc)
+               { }
+
+               protected NamespaceEntry (NamespaceEntry parent, SourceFile file, string name, bool is_implicit, Location loc)
+               {
+                       this.parent = parent;
+                       this.file = file;
+                       this.IsImplicit = is_implicit;
+                       this.ID = ++next_id;
+
+                       if (!is_implicit && (parent != null))
+                               ns = parent.NS.GetNamespace (name, true);
+                       else if (name != null)
+                               ns = Namespace.LookupNamespace (name, true);
+                       else
+                               ns = Namespace.Root;
+                       ns.AddNamespaceEntry (this);
+
+                       if ((parent != null) && (parent.NS != ns.Parent))
+                               implicit_parent = new NamespaceEntry (parent, file, ns.Parent.Name, true, loc);
+                       else
+                               implicit_parent = parent;
+
+                       this.FullName = ns.Name;
+               }
+
+               static int next_id = 0;
+               public readonly string FullName;
+               public readonly int ID;
+               public readonly bool IsImplicit;
+
+               public Namespace NS {
+                       get {
+                               return ns;
+                       }
+               }
+
+               public NamespaceEntry Parent {
+                       get {
+                               return parent;
+                       }
+               }
+
+               public NamespaceEntry ImplicitParent {
+                       get {
+                               return implicit_parent;
+                       }
+               }
+
+               public void DefineName (string name, object o)
+               {
+                       ns.DefineName (name, o);
+               }
+
                /// <summary>
                ///   Records a new namespace for resolving name references
                /// </summary>
@@ -91,6 +329,9 @@ namespace Mono.CSharp {
                                return;
                        }
 
+                       if (ns == FullName)
+                               return;
+                       
                        if (using_clauses == null)
                                using_clauses = new ArrayList ();
 
@@ -98,21 +339,15 @@ namespace Mono.CSharp {
                                if (old_entry.Name == ns){
                                        Report.Warning (105, loc, "The using directive for '" + ns +
                                                        "' appeared previously in this namespace");
-                                       break;
+                                       return;
                                }
                        }
                        
-                       UsingEntry ue = new UsingEntry (ns, loc);
+                       UsingEntry ue = new UsingEntry (this, ns, loc);
                        using_clauses.Add (ue);
                }
 
-               public ArrayList UsingTable {
-                       get {
-                               return using_clauses;
-                       }
-               }
-
-               public void UsingAlias (string alias, string namespace_or_type, Location loc)
+               public void UsingAlias (string alias, Expression namespace_or_type, Location loc)
                {
                        if (aliases == null)
                                aliases = new Hashtable ();
@@ -122,66 +357,240 @@ namespace Mono.CSharp {
                                              "' appeared previously in this namespace");
                                return;
                        }
-                                       
-                       aliases [alias] = namespace_or_type;
+
+                       aliases [alias] = new AliasEntry (this, alias, namespace_or_type, loc);
+               }
+
+               protected AliasEntry GetAliasEntry (string alias)
+               {
+                       AliasEntry entry = null;
+
+                       if (aliases != null)
+                               entry = (AliasEntry) aliases [alias];
+                       if (entry == null && Parent != null)
+                               entry = Parent.GetAliasEntry (alias);
+
+                       return entry;
                }
 
                public string LookupAlias (string alias)
                {
-                       string value = null;
+                       AliasEntry entry = GetAliasEntry (alias);
 
-                       // System.Console.WriteLine ("Lookup " + alias + " in " + name);
+                       if (entry == null)
+                               return null;
 
-                       if (aliases != null)
-                               value = (string) (aliases [alias]);
-                       if (value == null && Parent != null)
-                               value = Parent.LookupAlias (alias);
+                       object resolved = entry.Resolve ();
+                       if (resolved == null)
+                               return null;
+                       else if (resolved is Namespace)
+                               return ((Namespace) resolved).Name;
+                       else
+                               return ((Type) resolved).FullName;
+               }
+
+               public object Lookup (DeclSpace ds, string name, bool silent, Location loc)
+               {
+                       object o;
+                       Namespace ns;
+
+                       //
+                       // If name is of the form `N.I', first lookup `N', then search a member `I' in it.
+                       //
+                       int pos = name.IndexOf ('.');
+                       if (pos >= 0) {
+                               string first = name.Substring (0, pos);
+                               string last = name.Substring (pos + 1);
+
+                               o = Lookup (ds, first, silent, loc);
+                               if (o == null)
+                                       return null;
+
+                               ns = o as Namespace;
+                               if (ns != null)
+                                       return ns.Lookup (ds, last);
+
+                               Type nested = TypeManager.LookupType ((((Type) o).Name + "." + last));
+                               if ((nested == null) || ((ds != null) && !ds.CheckAccessLevel (nested)))
+                                       return null;
+
+                               return nested;
+                       }
+
+                       //
+                       // Check whether it's a namespace.
+                       //
+                       o = NS.Lookup (ds, name);
+                       if (o != null)
+                               return o;
+
+                       //
+                       // Check aliases.
+                       //
+                       AliasEntry entry = GetAliasEntry (name);
+                       if (entry != null) {
+                               o = entry.Resolve ();
+                               if (o != null)
+                                       return o;
+                       }
+
+                       if (name.IndexOf ('.') > 0)
+                               return null;
 
-                       return value;
+                       //
+                       // Check using entries.
+                       //
+                       Type t = null, match = null;
+                       foreach (Namespace using_ns in GetUsingTable ()) {
+                               match = using_ns.Lookup (ds, name) as Type;
+                               if (match != null){
+                                       if (t != null) {
+                                               if (!silent)
+                                                       DeclSpace.Error_AmbiguousTypeReference (loc, name, t, match);
+                                               return null;
+                                       } else {
+                                               t = match;
+                                       }
+                               }
+                       }
+
+                       return t;
+               }
+
+               // Our cached computation.
+               Namespace [] namespace_using_table;
+               public Namespace[] GetUsingTable ()
+               {
+                       if (namespace_using_table != null)
+                               return namespace_using_table;
+                       
+                       if (using_clauses == null)
+                               return new Namespace [0];
+
+                       ArrayList list = new ArrayList (using_clauses.Count);
+
+                       foreach (UsingEntry ue in using_clauses) {
+                               Namespace using_ns = ue.Resolve ();
+                               if (using_ns == null)
+                                       continue;
+
+                               list.Add (using_ns);
+                       }
+
+                       namespace_using_table = new Namespace [list.Count];
+                       list.CopyTo (namespace_using_table, 0);
+                       return namespace_using_table;
+               }
+
+               public void DefineNamespace (SymbolWriter symwriter)
+               {
+                       if (symfile_id != 0)
+                               return;
+                       if (parent != null)
+                               parent.DefineNamespace (symwriter);
+
+                       string[] using_list;
+                       if (using_clauses != null) {
+                               using_list = new string [using_clauses.Count];
+                               for (int i = 0; i < using_clauses.Count; i++)
+                                       using_list [i] = ((UsingEntry) using_clauses [i]).Name;
+                       } else {
+                               using_list = new string [0];
+                       }
+
+                       int parent_id = parent != null ? parent.symfile_id : 0;
+                       symfile_id = symwriter.DefineNamespace (ns.Name, file, using_list, parent_id);
+               }
+
+               public int SymbolFileID {
+                       get {
+                               return symfile_id;
+                       }
+               }
+
+               static void Msgtry (string s)
+               {
+                       Console.WriteLine ("    Try using -r:" + s);
+               }
+
+               protected void error246 (Location loc, string name)
+               {
+                       if (TypeManager.LookupType (name) != null)
+                               Report.Error (138, loc, "The using keyword only lets you specify a namespace, " +
+                                             "`" + name + "' is a class not a namespace.");
+                       else {
+                               Report.Error (246, loc, "The namespace `" + name +
+                                             "' can not be found (missing assembly reference?)");
+
+                               switch (name){
+                               case "Gtk": case "GtkSharp":
+                                       Msgtry ("gtk-sharp");
+                                       break;
+
+                               case "Gdk": case "GdkSharp":
+                                       Msgtry ("gdk-sharp");
+                                       break;
+
+                               case "Glade": case "GladeSharp":
+                                       Msgtry ("glade-sharp");
+                                       break;
+                                                       
+                               case "System.Drawing":
+                                       Msgtry ("System.Drawing");
+                                       break;
+                                                       
+                               case "System.Web.Services":
+                                       Msgtry ("System.Web.Services");
+                                       break;
+
+                               case "System.Web":
+                                       Msgtry ("System.Web");
+                                       break;
+                                                       
+                               case "System.Data":
+                                       Msgtry ("System.Data");
+                                       break;
+
+                               case "System.Windows.Forms":
+                                       Msgtry ("System.Windows.Forms");
+                                       break;
+                               }
+                       }
                }
 
                /// <summary>
                ///   Used to validate that all the using clauses are correct
                ///   after we are finished parsing all the files.  
                /// </summary>
-               public static bool VerifyUsing ()
+               public void VerifyUsing ()
                {
-                       ArrayList unused = new ArrayList ();
-                       int errors = 0;
-                       
-                       foreach (Namespace ns in all_namespaces){
-                               ArrayList uses = ns.UsingTable;
-                               if (uses == null)
-                                       continue;
-                               
-                               foreach (UsingEntry ue in uses){
-                                       if (ue.Used)
+                       if (using_clauses != null){
+                               foreach (UsingEntry ue in using_clauses){
+                                       if (ue.Resolve () != null)
                                                continue;
-                                       unused.Add (ue);
+
+                                       error246 (ue.Location, ue.Name);
                                }
                        }
 
-                       //
-                       // If we have unused using aliases, load all namespaces and check
-                       // whether it is unused, or it was missing
-                       //
-                       if (unused.Count > 0){
-                               Hashtable namespaces = TypeManager.GetNamespaces ();
+                       if (aliases != null){
+                               foreach (DictionaryEntry de in aliases){
+                                       AliasEntry alias = (AliasEntry) de.Value;
 
-                               foreach (UsingEntry ue in unused){
-                                       if (namespaces.Contains (ue.Name)){
-                                               Report.Warning (6024, ue.Location, "Unused namespace in `using' declaration");
+                                       if (alias.Resolve () != null)
                                                continue;
-                                       }
 
-                                       errors++;
-                                       Report.Error (246, ue.Location, "The namespace `" + ue.Name +
-                                                     "' can not be found (missing assembly reference?)");
+                                       error246 (alias.Location, alias.Alias.ToString ());
                                }
                        }
-                       
-                       return errors == 0;
                }
 
+               public override string ToString ()
+               {
+                       if (NS == Namespace.Root)
+                               return "NamespaceEntry (<root>)";
+                       else
+                               return String.Format ("NamespaceEntry ({0},{1},{2})", FullName, IsImplicit, ID);
+               }
        }
 }