2 // namespace.cs: Tracks namespaces
5 // Miguel de Icaza (miguel@ximian.com)
7 // (C) 2001 Ximian, Inc.
10 using System.Collections;
12 namespace Mono.CSharp {
15 /// Keeps track of the namespaces defined in the C# code.
17 public class Namespace : IAlias {
18 static ArrayList all_namespaces = new ArrayList ();
19 static Hashtable namespaces_map = new Hashtable ();
25 Hashtable defined_names;
28 /// Constructor Takes the current namespace and the
29 /// name. This is bootstrapped with parent == null
32 public Namespace (Namespace parent, string name)
36 string pname = parent != null ? parent.Name : "";
41 fullname = parent.Name + "." + name;
43 entries = new ArrayList ();
44 namespaces = new Hashtable ();
45 defined_names = new Hashtable ();
47 all_namespaces.Add (this);
48 if (namespaces_map.Contains (fullname))
50 namespaces_map [fullname] = true;
53 public static bool IsNamespace (string name)
55 return namespaces_map [name] != null;
58 public static Namespace Root = new Namespace (null, "");
60 public Namespace GetNamespace (string name, bool create)
62 int pos = name.IndexOf ('.');
67 first = name.Substring (0, pos);
71 ns = (Namespace) namespaces [first];
76 ns = new Namespace (this, first);
77 namespaces.Add (first, ns);
81 ns = ns.GetNamespace (name.Substring (pos + 1), create);
86 public static Namespace LookupNamespace (string name, bool create)
88 return Root.GetNamespace (name, create);
91 public IAlias Lookup (DeclSpace ds, string name, Location loc)
93 IAlias o = Lookup (name);
96 DeclSpace tdecl = o as DeclSpace;
98 t = tdecl.DefineType ();
100 if ((ds == null) || ds.CheckAccessLevel (t))
101 return new TypeExpression (t, loc);
104 Namespace ns = GetNamespace (name, false);
108 t = TypeManager.LookupType (DeclSpace.MakeFQN (fullname, name));
109 if ((t == null) || ((ds != null) && !ds.CheckAccessLevel (t)))
112 return new TypeExpression (t, loc);
115 public void AddNamespaceEntry (NamespaceEntry entry)
120 public void DefineName (string name, IAlias o)
122 defined_names.Add (name, o);
125 public IAlias Lookup (string name)
127 return (IAlias) defined_names [name];
130 static public ArrayList UserDefinedNamespaces {
132 return all_namespaces;
137 /// The qualified name of the current namespace
146 /// The parent of this namespace, used by the parser to "Pop"
147 /// the current namespace declaration
149 public Namespace Parent {
155 public static void DefineNamespaces (SymbolWriter symwriter)
157 foreach (Namespace ns in all_namespaces) {
158 foreach (NamespaceEntry entry in ns.entries)
159 entry.DefineNamespace (symwriter);
164 /// Used to validate that all the using clauses are correct
165 /// after we are finished parsing all the files.
167 public static void VerifyUsing ()
169 foreach (Namespace ns in all_namespaces) {
170 foreach (NamespaceEntry entry in ns.entries)
171 entry.VerifyUsing ();
175 public override string ToString ()
178 return "Namespace (<root>)";
180 return String.Format ("Namespace ({0})", Name);
184 get { return false; }
187 TypeExpr IAlias.ResolveAsType (EmitContext ec)
189 throw new InvalidOperationException ();
193 public class NamespaceEntry
196 NamespaceEntry parent, implicit_parent;
200 ArrayList using_clauses;
201 public bool DeclarationFound = false;
204 // This class holds the location where a using definition is
205 // done, and whether it has been used by the program or not.
207 // We use this to flag using clauses for namespaces that do not
210 public class UsingEntry {
211 public readonly string Name;
212 public readonly NamespaceEntry NamespaceEntry;
213 public readonly Location Location;
215 public UsingEntry (NamespaceEntry entry, string name, Location loc)
218 NamespaceEntry = entry;
222 Namespace resolved_ns;
224 public Namespace Resolve ()
226 if (resolved_ns != null)
229 object resolved = NamespaceEntry.LookupForUsing (Name, Location);
230 resolved_ns = resolved as Namespace;
235 public class AliasEntry {
236 public readonly string Name;
237 public readonly MemberName Alias;
238 public readonly NamespaceEntry NamespaceEntry;
239 public readonly Location Location;
241 public AliasEntry (NamespaceEntry entry, string name, MemberName alias, Location loc)
245 NamespaceEntry = entry;
251 public IAlias Resolve ()
253 if (resolved != null)
256 string alias = Alias.GetPartialName ();
258 resolved = NamespaceEntry.LookupForUsing (alias, Location);
263 public NamespaceEntry (NamespaceEntry parent, SourceFile file, string name, Location loc)
264 : this (parent, file, name, false, loc)
267 protected NamespaceEntry (NamespaceEntry parent, SourceFile file, string name, bool is_implicit, Location loc)
269 this.parent = parent;
271 this.IsImplicit = is_implicit;
274 if (!is_implicit && (parent != null))
275 ns = parent.NS.GetNamespace (name, true);
276 else if (name != null)
277 ns = Namespace.LookupNamespace (name, true);
280 ns.AddNamespaceEntry (this);
282 if ((parent != null) && (parent.NS != ns.Parent))
283 implicit_parent = new NamespaceEntry (parent, file, ns.Parent.Name, true, loc);
285 implicit_parent = parent;
287 this.FullName = ns.Name;
290 static int next_id = 0;
291 public readonly string FullName;
292 public readonly int ID;
293 public readonly bool IsImplicit;
295 public Namespace NS {
301 public NamespaceEntry Parent {
307 public NamespaceEntry ImplicitParent {
309 return implicit_parent;
313 public void DefineName (string name, IAlias o)
315 ns.DefineName (name, o);
319 /// Records a new namespace for resolving name references
321 public void Using (string ns, Location loc)
323 if (DeclarationFound){
324 Report.Error (1529, loc, "A using clause must precede all other namespace elements");
331 if (using_clauses == null)
332 using_clauses = new ArrayList ();
334 foreach (UsingEntry old_entry in using_clauses) {
335 if (old_entry.Name == ns) {
336 if (RootContext.WarningLevel >= 3)
337 Report.Warning (105, loc, "The using directive for '{0}' appeared previously in this namespace", ns);
342 UsingEntry ue = new UsingEntry (this, ns, loc);
343 using_clauses.Add (ue);
346 public void UsingAlias (string name, MemberName alias, Location loc)
348 if (DeclarationFound){
349 Report.Error (1529, loc, "A using clause must precede all other namespace elements");
354 aliases = new Hashtable ();
356 if (aliases.Contains (name)){
357 Report.Error (1537, loc, "The using alias `" + name +
358 "' appeared previously in this namespace");
362 aliases [name] = new AliasEntry (this, name, alias, loc);
365 protected AliasEntry GetAliasEntry (string alias)
367 AliasEntry entry = null;
370 entry = (AliasEntry) aliases [alias];
371 if (entry == null && Parent != null)
372 entry = Parent.GetAliasEntry (alias);
377 public IAlias LookupAlias (string alias)
379 AliasEntry entry = GetAliasEntry (alias);
384 return entry.Resolve ();
388 // According to section 16.3.1 (using-alias-directive), the namespace-or-type-name is
389 // resolved as if the immediately containing namespace body has no using-directives.
391 // Section 16.3.2 says that the same rule is applied when resolving the namespace-name
392 // in the using-namespace-directive.
394 public IAlias LookupForUsing (string dotted_name, Location loc)
396 int pos = dotted_name.IndexOf ('.');
397 string simple_name = dotted_name;
400 simple_name = dotted_name.Substring (0, pos);
401 rest = dotted_name.Substring (pos + 1);
404 IAlias o = NS.Lookup (null, simple_name, loc);
405 if (o == null && ImplicitParent != null)
406 o = ImplicitParent.LookupNamespaceOrType (null, simple_name, loc);
408 if (o == null || rest == null)
411 Namespace ns = o as Namespace;
413 return ns.Lookup (null, rest, loc);
415 Type nested = TypeManager.LookupType (o.Name + "." + rest);
419 return new TypeExpression (nested, loc);
422 public IAlias LookupNamespaceOrType (DeclSpace ds, string name, Location loc)
424 IAlias resolved = null;
425 for (NamespaceEntry curr_ns = this; curr_ns != null; curr_ns = curr_ns.ImplicitParent) {
426 if ((resolved = curr_ns.Lookup (ds, name, loc)) != null)
432 private IAlias Lookup (DeclSpace ds, string name, Location loc)
438 // If name is of the form `N.I', first lookup `N', then search a member `I' in it.
440 // FIXME: Remove this block. Only simple names should come here.
441 // The bug: The loop in LookupNamespaceOrType continues if
442 // the lookup for N succeeds but the nested lookup for I fails.
443 // This is one part of #52697.
445 int pos = name.IndexOf ('.');
447 string first = name.Substring (0, pos);
448 string last = name.Substring (pos + 1);
450 o = Lookup (ds, first, loc);
456 return ns.Lookup (ds, last, loc);
458 Type nested = TypeManager.LookupType (o.Name + "." + last);
459 if ((nested == null) || ((ds != null) && !ds.CheckAccessLevel (nested)))
462 return new TypeExpression (nested, loc);
466 // Check whether it's in the namespace.
468 o = NS.Lookup (ds, name, loc);
475 AliasEntry entry = GetAliasEntry (name);
477 o = entry.Resolve ();
482 if (name.IndexOf ('.') > 0)
486 // Check using entries.
488 IAlias t = null, match = null;
489 foreach (Namespace using_ns in GetUsingTable ()) {
490 match = using_ns.Lookup (ds, name, loc);
491 if ((match != null) && match.IsType){
493 DeclSpace.Error_AmbiguousTypeReference (loc, name, t.Name, match.Name);
504 // Our cached computation.
505 Namespace [] namespace_using_table;
506 public Namespace[] GetUsingTable ()
508 if (namespace_using_table != null)
509 return namespace_using_table;
511 if (using_clauses == null)
512 return new Namespace [0];
514 ArrayList list = new ArrayList (using_clauses.Count);
516 foreach (UsingEntry ue in using_clauses) {
517 Namespace using_ns = ue.Resolve ();
518 if (using_ns == null)
524 namespace_using_table = new Namespace [list.Count];
525 list.CopyTo (namespace_using_table, 0);
526 return namespace_using_table;
529 public void DefineNamespace (SymbolWriter symwriter)
534 parent.DefineNamespace (symwriter);
537 if (using_clauses != null) {
538 using_list = new string [using_clauses.Count];
539 for (int i = 0; i < using_clauses.Count; i++)
540 using_list [i] = ((UsingEntry) using_clauses [i]).Name;
542 using_list = new string [0];
545 int parent_id = parent != null ? parent.symfile_id : 0;
546 if (file.SourceFileEntry == null)
549 symfile_id = symwriter.DefineNamespace (
550 ns.Name, file.SourceFileEntry, using_list, parent_id);
553 public int SymbolFileID {
559 static void MsgtryRef (string s)
561 Console.WriteLine (" Try using -r:" + s);
564 static void MsgtryPkg (string s)
566 Console.WriteLine (" Try using -pkg:" + s);
569 protected void error246 (Location loc, string name)
571 Report.Error (246, loc, "The namespace `" + name +
572 "' can not be found (missing assembly reference?)");
575 case "Gtk": case "GtkSharp":
576 MsgtryPkg ("gtk-sharp");
579 case "Gdk": case "GdkSharp":
580 MsgtryPkg ("gdk-sharp");
583 case "Glade": case "GladeSharp":
584 MsgtryPkg ("glade-sharp");
587 case "System.Drawing":
588 case "System.Web.Services":
591 case "System.Windows.Forms":
598 /// Used to validate that all the using clauses are correct
599 /// after we are finished parsing all the files.
601 public void VerifyUsing ()
603 if (using_clauses != null){
604 foreach (UsingEntry ue in using_clauses){
605 if (ue.Resolve () != null)
608 if (LookupForUsing (ue.Name, ue.Location) == null)
609 error246 (ue.Location, ue.Name);
611 Report.Error (138, ue.Location, "The using keyword only lets you specify a namespace, " +
612 "`" + ue.Name + "' is a class not a namespace.");
617 if (aliases != null){
618 foreach (DictionaryEntry de in aliases){
619 AliasEntry alias = (AliasEntry) de.Value;
621 if (alias.Resolve () != null)
624 error246 (alias.Location, alias.Alias.GetPartialName ());
629 public override string ToString ()
631 if (NS == Namespace.Root)
632 return "NamespaceEntry (<root>)";
634 return String.Format ("NamespaceEntry ({0},{1},{2})", FullName, IsImplicit, ID);