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