2003-10-29 Ben Maurer <bmaurer@users.sourceforge.net>
[mono.git] / mcs / gmcs / namespace.cs
1 //
2 // namespace.cs: Tracks namespaces
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //
7 // (C) 2001 Ximian, Inc.
8 //
9 using System;
10 using System.Collections;
11
12 namespace Mono.CSharp {
13
14         /// <summary>
15         ///   Keeps track of the namespaces defined in the C# code.
16         /// </summary>
17         public class Namespace {
18                 static ArrayList all_namespaces = new ArrayList ();
19                 static Hashtable namespaces_map = new Hashtable ();
20                 
21                 Namespace parent;
22                 string name, fullname;
23                 ArrayList entries;
24                 Hashtable namespaces;
25                 Hashtable defined_names;
26
27                 /// <summary>
28                 ///   Constructor Takes the current namespace and the
29                 ///   name.  This is bootstrapped with parent == null
30                 ///   and name = ""
31                 /// </summary>
32                 public Namespace (Namespace parent, string name)
33                 {
34                         this.name = name;
35                         this.parent = parent;
36
37                         string pname = parent != null ? parent.Name : "";
38                                 
39                         if (pname == "")
40                                 fullname = name;
41                         else
42                                 fullname = parent.Name + "." + name;
43
44                         entries = new ArrayList ();
45                         namespaces = new Hashtable ();
46                         defined_names = new Hashtable ();
47
48                         all_namespaces.Add (this);
49                         if (namespaces_map.Contains (fullname))
50                                 return;
51                         namespaces_map [fullname] = true;
52                 }
53
54                 public static bool IsNamespace (string name)
55                 {
56                         return namespaces_map [name] != null;
57                 }
58                 
59                 public static Namespace Root = new Namespace (null, "");
60
61                 public Namespace GetNamespace (string name, bool create)
62                 {
63                         int pos = name.IndexOf ('.');
64
65                         Namespace ns;
66                         string first;
67                         if (pos >= 0)
68                                 first = name.Substring (0, pos);
69                         else
70                                 first = name;
71
72                         ns = (Namespace) namespaces [first];
73                         if (ns == null) {
74                                 if (!create)
75                                         return null;
76
77                                 ns = new Namespace (this, first);
78                                 namespaces.Add (first, ns);
79                         }
80
81                         if (pos >= 0)
82                                 ns = ns.GetNamespace (name.Substring (pos + 1), create);
83
84                         return ns;
85                 }
86
87                 public static Namespace LookupNamespace (string name, bool create)
88                 {
89                         return Root.GetNamespace (name, create);
90                 }
91
92                 public object Lookup (DeclSpace ds, string name)
93                 {
94                         object o = Lookup (name);
95
96                         Type t;
97                         DeclSpace tdecl = o as DeclSpace;
98                         if (tdecl != null) {
99                                 t = tdecl.DefineType ();
100
101                                 if ((ds == null) || ds.CheckAccessLevel (t))
102                                         return t;
103                         }
104
105                         Namespace ns = GetNamespace (name, false);
106                         if (ns != null)
107                                 return ns;
108
109                         t = TypeManager.LookupType (DeclSpace.MakeFQN (fullname, name));
110                         if ((t == null) || ((ds != null) && !ds.CheckAccessLevel (t)))
111                                 return null;
112
113                         return t;
114                 }
115
116                 public void AddNamespaceEntry (NamespaceEntry entry)
117                 {
118                         entries.Add (entry);
119                 }
120
121                 public void DefineName (string name, object o)
122                 {
123                         defined_names.Add (name, o);
124                 }
125
126                 public object Lookup (string name)
127                 {
128                         return defined_names [name];
129                 }
130
131                 static public ArrayList UserDefinedNamespaces {
132                         get {
133                                 return all_namespaces;
134                         }
135                 }
136
137                 /// <summary>
138                 ///   The qualified name of the current namespace
139                 /// </summary>
140                 public string Name {
141                         get {
142                                 return fullname;
143                         }
144                 }
145
146                 /// <summary>
147                 ///   The parent of this namespace, used by the parser to "Pop"
148                 ///   the current namespace declaration
149                 /// </summary>
150                 public Namespace Parent {
151                         get {
152                                 return parent;
153                         }
154                 }
155
156                 public static void DefineNamespaces (SymbolWriter symwriter)
157                 {
158                         foreach (Namespace ns in all_namespaces) {
159                                 foreach (NamespaceEntry entry in ns.entries)
160                                         entry.DefineNamespace (symwriter);
161                         }
162                 }
163
164                 /// <summary>
165                 ///   Used to validate that all the using clauses are correct
166                 ///   after we are finished parsing all the files.  
167                 /// </summary>
168                 public static void VerifyUsing ()
169                 {
170                         foreach (Namespace ns in all_namespaces) {
171                                 foreach (NamespaceEntry entry in ns.entries)
172                                         entry.VerifyUsing ();
173                         }
174                 }
175
176                 public override string ToString ()
177                 {
178                         if (this == Root)
179                                 return "Namespace (<root>)";
180                         else
181                                 return String.Format ("Namespace ({0})", Name);
182                 }
183         }
184
185         public class NamespaceEntry
186         {
187                 Namespace ns;
188                 NamespaceEntry parent, implicit_parent;
189                 SourceFile file;
190                 int symfile_id;
191                 Hashtable aliases;
192                 ArrayList using_clauses;
193                 public bool DeclarationFound = false;
194
195                 //
196                 // This class holds the location where a using definition is
197                 // done, and whether it has been used by the program or not.
198                 //
199                 // We use this to flag using clauses for namespaces that do not
200                 // exist.
201                 //
202                 public class UsingEntry {
203                         public readonly string Name;
204                         public readonly NamespaceEntry NamespaceEntry;
205                         public readonly Location Location;
206                         
207                         public UsingEntry (NamespaceEntry entry, string name, Location loc)
208                         {
209                                 Name = name;
210                                 NamespaceEntry = entry;
211                                 Location = loc;
212                         }
213
214                         Namespace resolved_ns;
215
216                         public Namespace Resolve ()
217                         {
218                                 if (resolved_ns != null)
219                                         return resolved_ns;
220
221                                 Namespace curr_ns = NamespaceEntry.NS;
222                                 while ((curr_ns != null) && (resolved_ns == null)) {
223                                         resolved_ns = curr_ns.GetNamespace (Name, false);
224
225                                         if (resolved_ns == null)
226                                                 curr_ns = curr_ns.Parent;
227                                 }
228
229                                 return resolved_ns;
230                         }
231                 }
232
233                 public class AliasEntry {
234                         public readonly string Name;
235                         public readonly Expression Alias;
236                         public readonly NamespaceEntry NamespaceEntry;
237                         public readonly Location Location;
238                         
239                         public AliasEntry (NamespaceEntry entry, string name, Expression alias, Location loc)
240                         {
241                                 Name = name;
242                                 Alias = alias;
243                                 NamespaceEntry = entry;
244                                 Location = loc;
245                         }
246
247                         object resolved;
248
249                         public object Resolve ()
250                         {
251                                 if (resolved != null)
252                                         return resolved;
253
254                                 NamespaceEntry curr_ns = NamespaceEntry;
255
256                                 //
257                                 // GENERICS: Cope with the expression and not with the string
258                                 // this will fail with `using A = Stack<int>'
259                                 //
260                                 
261                                 string alias = Alias.ToString ();
262                                 while ((curr_ns != null) && (resolved == null)) {
263                                         resolved = curr_ns.Lookup (null, alias, Location);
264
265                                         if (resolved == null)
266                                                 curr_ns = curr_ns.Parent;
267                                 }
268
269                                 return resolved;
270                         }
271                 }
272
273                 public NamespaceEntry (NamespaceEntry parent, SourceFile file, string name, Location loc)
274                         : this (parent, file, name, false, loc)
275                 { }
276
277                 protected NamespaceEntry (NamespaceEntry parent, SourceFile file, string name, bool is_implicit, Location loc)
278                 {
279                         this.parent = parent;
280                         this.file = file;
281                         this.IsImplicit = is_implicit;
282                         this.ID = ++next_id;
283
284                         if (!is_implicit && (parent != null))
285                                 ns = parent.NS.GetNamespace (name, true);
286                         else if (name != null)
287                                 ns = Namespace.LookupNamespace (name, true);
288                         else
289                                 ns = Namespace.Root;
290                         ns.AddNamespaceEntry (this);
291
292                         if ((parent != null) && (parent.NS != ns.Parent))
293                                 implicit_parent = new NamespaceEntry (parent, file, ns.Parent.Name, true, loc);
294                         else
295                                 implicit_parent = parent;
296
297                         this.FullName = ns.Name;
298                 }
299
300                 static int next_id = 0;
301                 public readonly string FullName;
302                 public readonly int ID;
303                 public readonly bool IsImplicit;
304
305                 public Namespace NS {
306                         get {
307                                 return ns;
308                         }
309                 }
310
311                 public NamespaceEntry Parent {
312                         get {
313                                 return parent;
314                         }
315                 }
316
317                 public NamespaceEntry ImplicitParent {
318                         get {
319                                 return implicit_parent;
320                         }
321                 }
322
323                 public void DefineName (string name, object o)
324                 {
325                         ns.DefineName (name, o);
326                 }
327
328                 /// <summary>
329                 ///   Records a new namespace for resolving name references
330                 /// </summary>
331                 public void Using (string ns, Location loc)
332                 {
333                         if (DeclarationFound){
334                                 Report.Error (1529, loc, "A using clause must precede all other namespace elements");
335                                 return;
336                         }
337
338                         if (ns == FullName)
339                                 return;
340                         
341                         if (using_clauses == null)
342                                 using_clauses = new ArrayList ();
343
344                         foreach (UsingEntry old_entry in using_clauses){
345                                 if (old_entry.Name == ns){
346                                         Report.Warning (105, loc, "The using directive for '" + ns +
347                                                         "' appeared previously in this namespace");
348                                         return;
349                                 }
350                         }
351                         
352                         UsingEntry ue = new UsingEntry (this, ns, loc);
353                         using_clauses.Add (ue);
354                 }
355
356                 public void UsingAlias (string alias, Expression namespace_or_type, Location loc)
357                 {
358                         if (aliases == null)
359                                 aliases = new Hashtable ();
360                         
361                         if (aliases.Contains (alias)){
362                                 Report.Error (1537, loc, "The using alias `" + alias +
363                                               "' appeared previously in this namespace");
364                                 return;
365                         }
366
367                         aliases [alias] = new AliasEntry (this, alias, namespace_or_type, loc);
368                 }
369
370                 protected AliasEntry GetAliasEntry (string alias)
371                 {
372                         AliasEntry entry = null;
373
374                         if (aliases != null)
375                                 entry = (AliasEntry) aliases [alias];
376                         if (entry == null && Parent != null)
377                                 entry = Parent.GetAliasEntry (alias);
378
379                         return entry;
380                 }
381
382                 public string LookupAlias (string alias)
383                 {
384                         AliasEntry entry = GetAliasEntry (alias);
385
386                         if (entry == null)
387                                 return null;
388
389                         object resolved = entry.Resolve ();
390                         if (resolved == null)
391                                 return null;
392                         else if (resolved is Namespace)
393                                 return ((Namespace) resolved).Name;
394                         else
395                                 return ((Type) resolved).FullName;
396                 }
397
398                 public object Lookup (DeclSpace ds, string name, Location loc)
399                 {
400                         object o;
401                         Namespace ns;
402
403                         //
404                         // If name is of the form `N.I', first lookup `N', then search a member `I' in it.
405                         //
406                         int pos = name.IndexOf ('.');
407                         if (pos >= 0) {
408                                 string first = name.Substring (0, pos);
409                                 string last = name.Substring (pos + 1);
410
411                                 o = Lookup (ds, first, loc);
412                                 if (o == null)
413                                         return null;
414
415                                 ns = o as Namespace;
416                                 if (ns != null)
417                                         return ns.Lookup (ds, last);
418
419                                 Type nested = TypeManager.LookupType ((((Type) o).Name + "." + last));
420                                 if ((nested == null) || ((ds != null) && !ds.CheckAccessLevel (nested)))
421                                         return null;
422
423                                 return nested;
424                         }
425
426                         //
427                         // Check whether it's a namespace.
428                         //
429                         o = NS.Lookup (ds, name);
430                         if (o != null)
431                                 return o;
432
433                         //
434                         // Check aliases.
435                         //
436                         AliasEntry entry = GetAliasEntry (name);
437                         if (entry != null) {
438                                 o = entry.Resolve ();
439                                 if (o != null)
440                                         return o;
441                         }
442
443                         //
444                         // Check using entries.
445                         //
446                         Type t = null, match = null;
447                         foreach (Namespace using_ns in GetUsingTable ()) {
448                                 match = using_ns.Lookup (ds, name) as Type;
449                                 if (match != null){
450                                         if (t != null) {
451                                                 DeclSpace.Error_AmbiguousTypeReference (loc, name, t, match);
452                                                 return null;
453                                         } else {
454                                                 t = match;
455                                         }
456                                 }
457                         }
458
459                         return t;
460                 }
461
462                 // Our cached computation.
463                 Namespace [] namespace_using_table;
464                 public Namespace[] GetUsingTable ()
465                 {
466                         if (namespace_using_table != null)
467                                 return namespace_using_table;
468                         
469                         if (using_clauses == null)
470                                 return new Namespace [0];
471
472                         ArrayList list = new ArrayList (using_clauses.Count);
473
474                         foreach (UsingEntry ue in using_clauses) {
475                                 Namespace using_ns = ue.Resolve ();
476                                 if (using_ns == null)
477                                         continue;
478
479                                 list.Add (using_ns);
480                         }
481
482                         namespace_using_table = new Namespace [list.Count];
483                         list.CopyTo (namespace_using_table, 0);
484                         return namespace_using_table;
485                 }
486
487                 public void DefineNamespace (SymbolWriter symwriter)
488                 {
489                         if (symfile_id != 0)
490                                 return;
491                         if (parent != null)
492                                 parent.DefineNamespace (symwriter);
493
494                         string[] using_list;
495                         if (using_clauses != null) {
496                                 using_list = new string [using_clauses.Count];
497                                 for (int i = 0; i < using_clauses.Count; i++)
498                                         using_list [i] = ((UsingEntry) using_clauses [i]).Name;
499                         } else {
500                                 using_list = new string [0];
501                         }
502
503                         int parent_id = parent != null ? parent.symfile_id : 0;
504                         symfile_id = symwriter.DefineNamespace (ns.Name, file, using_list, parent_id);
505                 }
506
507                 public int SymbolFileID {
508                         get {
509                                 return symfile_id;
510                         }
511                 }
512
513                 static void Msgtry (string s)
514                 {
515                         Console.WriteLine ("    Try using -r:" + s);
516                 }
517
518                 protected void error246 (Location loc, string name)
519                 {
520                         if (TypeManager.LookupType (name) != null)
521                                 Report.Error (138, loc, "The using keyword only lets you specify a namespace, " +
522                                               "`" + name + "' is a class not a namespace.");
523                         else {
524                                 Report.Error (246, loc, "The namespace `" + name +
525                                               "' can not be found (missing assembly reference?)");
526
527                                 switch (name){
528                                 case "Gtk": case "GtkSharp":
529                                         Msgtry ("gtk-sharp");
530                                         break;
531
532                                 case "Gdk": case "GdkSharp":
533                                         Msgtry ("gdk-sharp");
534                                         break;
535
536                                 case "Glade": case "GladeSharp":
537                                         Msgtry ("glade-sharp");
538                                         break;
539                                                         
540                                 case "System.Drawing":
541                                         Msgtry ("System.Drawing");
542                                         break;
543                                                         
544                                 case "System.Web.Services":
545                                         Msgtry ("System.Web.Services");
546                                         break;
547
548                                 case "System.Web":
549                                         Msgtry ("System.Web");
550                                         break;
551                                                         
552                                 case "System.Data":
553                                         Msgtry ("System.Data");
554                                         break;
555
556                                 case "System.Windows.Forms":
557                                         Msgtry ("System.Windows.Forms");
558                                         break;
559                                 }
560                         }
561                 }
562
563                 /// <summary>
564                 ///   Used to validate that all the using clauses are correct
565                 ///   after we are finished parsing all the files.  
566                 /// </summary>
567                 public void VerifyUsing ()
568                 {
569                         if (using_clauses != null){
570                                 foreach (UsingEntry ue in using_clauses){
571                                         if (ue.Resolve () != null)
572                                                 continue;
573
574                                         error246 (ue.Location, ue.Name);
575                                 }
576                         }
577
578                         if (aliases != null){
579                                 foreach (DictionaryEntry de in aliases){
580                                         AliasEntry alias = (AliasEntry) de.Value;
581
582                                         if (alias.Resolve () != null)
583                                                 continue;
584
585                                         error246 (alias.Location, alias.Alias.ToString ());
586                                 }
587                         }
588                 }
589
590                 public override string ToString ()
591                 {
592                         if (NS == Namespace.Root)
593                                 return "NamespaceEntry (<root>)";
594                         else
595                                 return String.Format ("NamespaceEntry ({0},{1},{2})", FullName, IsImplicit, ID);
596                 }
597         }
598 }