2004-01-07 Miguel de Icaza <miguel@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 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.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 string Alias;
236                         public readonly NamespaceEntry NamespaceEntry;
237                         public readonly Location Location;
238                         
239                         public AliasEntry (NamespaceEntry entry, string name, string 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                                 while ((curr_ns != null) && (resolved == null)) {
256                                         resolved = curr_ns.Lookup (null, Alias, Location);
257
258                                         if (resolved == null)
259                                                 curr_ns = curr_ns.Parent;
260                                 }
261
262                                 return resolved;
263                         }
264                 }
265
266                 public NamespaceEntry (NamespaceEntry parent, SourceFile file, string name, Location loc)
267                         : this (parent, file, name, false, loc)
268                 { }
269
270                 protected NamespaceEntry (NamespaceEntry parent, SourceFile file, string name, bool is_implicit, Location loc)
271                 {
272                         this.parent = parent;
273                         this.file = file;
274                         this.IsImplicit = is_implicit;
275                         this.ID = ++next_id;
276
277                         if (!is_implicit && (parent != null))
278                                 ns = parent.NS.GetNamespace (name, true);
279                         else if (name != null)
280                                 ns = Namespace.LookupNamespace (name, true);
281                         else
282                                 ns = Namespace.Root;
283                         ns.AddNamespaceEntry (this);
284
285                         if ((parent != null) && (parent.NS != ns.Parent))
286                                 implicit_parent = new NamespaceEntry (parent, file, ns.Parent.Name, true, loc);
287                         else
288                                 implicit_parent = parent;
289
290                         this.FullName = ns.Name;
291                 }
292
293                 static int next_id = 0;
294                 public readonly string FullName;
295                 public readonly int ID;
296                 public readonly bool IsImplicit;
297
298                 public Namespace NS {
299                         get {
300                                 return ns;
301                         }
302                 }
303
304                 public NamespaceEntry Parent {
305                         get {
306                                 return parent;
307                         }
308                 }
309
310                 public NamespaceEntry ImplicitParent {
311                         get {
312                                 return implicit_parent;
313                         }
314                 }
315
316                 public void DefineName (string name, object o)
317                 {
318                         ns.DefineName (name, o);
319                 }
320
321                 /// <summary>
322                 ///   Records a new namespace for resolving name references
323                 /// </summary>
324                 public void Using (string ns, Location loc)
325                 {
326                         if (DeclarationFound){
327                                 Report.Error (1529, loc, "A using clause must precede all other namespace elements");
328                                 return;
329                         }
330
331                         if (ns == FullName)
332                                 return;
333                         
334                         if (using_clauses == null)
335                                 using_clauses = new ArrayList ();
336
337                         foreach (UsingEntry old_entry in using_clauses){
338                                 if (old_entry.Name == ns){
339                                         Report.Warning (105, loc, "The using directive for '" + ns +
340                                                         "' appeared previously in this namespace");
341                                         return;
342                                 }
343                         }
344                         
345                         UsingEntry ue = new UsingEntry (this, ns, loc);
346                         using_clauses.Add (ue);
347                 }
348
349                 public void UsingAlias (string alias, string namespace_or_type, Location loc)
350                 {
351                         if (aliases == null)
352                                 aliases = new Hashtable ();
353                         
354                         if (aliases.Contains (alias)){
355                                 Report.Error (1537, loc, "The using alias `" + alias +
356                                               "' appeared previously in this namespace");
357                                 return;
358                         }
359
360                         aliases [alias] = new AliasEntry (this, alias, namespace_or_type, loc);
361                 }
362
363                 protected AliasEntry GetAliasEntry (string alias)
364                 {
365                         AliasEntry entry = null;
366
367                         if (aliases != null)
368                                 entry = (AliasEntry) aliases [alias];
369                         if (entry == null && Parent != null)
370                                 entry = Parent.GetAliasEntry (alias);
371
372                         return entry;
373                 }
374
375                 public string LookupAlias (string alias)
376                 {
377                         AliasEntry entry = GetAliasEntry (alias);
378
379                         if (entry == null)
380                                 return null;
381
382                         object resolved = entry.Resolve ();
383                         if (resolved == null)
384                                 return null;
385                         else if (resolved is Namespace)
386                                 return ((Namespace) resolved).Name;
387                         else
388                                 return ((Type) resolved).FullName;
389                 }
390
391                 public object Lookup (DeclSpace ds, string name, Location loc)
392                 {
393                         object o;
394                         Namespace ns;
395
396                         //
397                         // If name is of the form `N.I', first lookup `N', then search a member `I' in it.
398                         //
399                         int pos = name.IndexOf ('.');
400                         if (pos >= 0) {
401                                 string first = name.Substring (0, pos);
402                                 string last = name.Substring (pos + 1);
403
404                                 o = Lookup (ds, first, loc);
405                                 if (o == null)
406                                         return null;
407
408                                 ns = o as Namespace;
409                                 if (ns != null)
410                                         return ns.Lookup (ds, last);
411
412                                 Type nested = TypeManager.LookupType ((((Type) o).Name + "." + last));
413                                 if ((nested == null) || ((ds != null) && !ds.CheckAccessLevel (nested)))
414                                         return null;
415
416                                 return nested;
417                         }
418
419                         //
420                         // Check whether it's a namespace.
421                         //
422                         o = NS.Lookup (ds, name);
423                         if (o != null)
424                                 return o;
425
426                         //
427                         // Check aliases.
428                         //
429                         AliasEntry entry = GetAliasEntry (name);
430                         if (entry != null) {
431                                 o = entry.Resolve ();
432                                 if (o != null)
433                                         return o;
434                         }
435
436                         //
437                         // Check using entries.
438                         //
439                         Type t = null, match = null;
440                         foreach (Namespace using_ns in GetUsingTable ()) {
441                                 match = using_ns.Lookup (ds, name) as Type;
442                                 if (match != null){
443                                         if (t != null) {
444                                                 DeclSpace.Error_AmbiguousTypeReference (loc, name, t, match);
445                                                 return null;
446                                         } else {
447                                                 t = match;
448                                         }
449                                 }
450                         }
451
452                         return t;
453                 }
454
455                 // Our cached computation.
456                 Namespace [] namespace_using_table;
457                 public Namespace[] GetUsingTable ()
458                 {
459                         if (namespace_using_table != null)
460                                 return namespace_using_table;
461                         
462                         if (using_clauses == null)
463                                 return new Namespace [0];
464
465                         ArrayList list = new ArrayList (using_clauses.Count);
466
467                         foreach (UsingEntry ue in using_clauses) {
468                                 Namespace using_ns = ue.Resolve ();
469                                 if (using_ns == null)
470                                         continue;
471
472                                 list.Add (using_ns);
473                         }
474
475                         namespace_using_table = new Namespace [list.Count];
476                         list.CopyTo (namespace_using_table, 0);
477                         return namespace_using_table;
478                 }
479
480                 public void DefineNamespace (SymbolWriter symwriter)
481                 {
482                         if (symfile_id != 0)
483                                 return;
484                         if (parent != null)
485                                 parent.DefineNamespace (symwriter);
486
487                         string[] using_list;
488                         if (using_clauses != null) {
489                                 using_list = new string [using_clauses.Count];
490                                 for (int i = 0; i < using_clauses.Count; i++)
491                                         using_list [i] = ((UsingEntry) using_clauses [i]).Name;
492                         } else {
493                                 using_list = new string [0];
494                         }
495
496                         int parent_id = parent != null ? parent.symfile_id : 0;
497                         symfile_id = symwriter.DefineNamespace (ns.Name, file, using_list, parent_id);
498                 }
499
500                 public int SymbolFileID {
501                         get {
502                                 return symfile_id;
503                         }
504                 }
505
506                 static void Msgtry (string s)
507                 {
508                         Console.WriteLine ("    Try using -r:" + s);
509                 }
510
511                 protected void error246 (Location loc, string name)
512                 {
513                         if (TypeManager.LookupType (name) != null)
514                                 Report.Error (138, loc, "The using keyword only lets you specify a namespace, " +
515                                               "`" + name + "' is a class not a namespace.");
516                         else {
517                                 Report.Error (246, loc, "The namespace `" + name +
518                                               "' can not be found (missing assembly reference?)");
519
520                                 switch (name){
521                                 case "Gtk": case "GtkSharp":
522                                         Msgtry ("gtk-sharp");
523                                         break;
524
525                                 case "Gdk": case "GdkSharp":
526                                         Msgtry ("gdk-sharp");
527                                         break;
528
529                                 case "Glade": case "GladeSharp":
530                                         Msgtry ("glade-sharp");
531                                         break;
532                                                         
533                                 case "System.Drawing":
534                                         Msgtry ("System.Drawing");
535                                         break;
536                                                         
537                                 case "System.Web.Services":
538                                         Msgtry ("System.Web.Services");
539                                         break;
540
541                                 case "System.Web":
542                                         Msgtry ("System.Web");
543                                         break;
544                                                         
545                                 case "System.Data":
546                                         Msgtry ("System.Data");
547                                         break;
548
549                                 case "System.Windows.Forms":
550                                         Msgtry ("System.Windows.Forms");
551                                         break;
552                                 }
553                         }
554                 }
555
556                 /// <summary>
557                 ///   Used to validate that all the using clauses are correct
558                 ///   after we are finished parsing all the files.  
559                 /// </summary>
560                 public void VerifyUsing ()
561                 {
562                         if (using_clauses != null){
563                                 foreach (UsingEntry ue in using_clauses){
564                                         if (ue.Resolve () != null)
565                                                 continue;
566
567                                         error246 (ue.Location, ue.Name);
568                                 }
569                         }
570
571                         if (aliases != null){
572                                 foreach (DictionaryEntry de in aliases){
573                                         AliasEntry alias = (AliasEntry) de.Value;
574
575                                         if (alias.Resolve () != null)
576                                                 continue;
577
578                                         error246 (alias.Location, alias.Alias);
579                                 }
580                         }
581                 }
582
583                 public override string ToString ()
584                 {
585                         if (NS == Namespace.Root)
586                                 return "NamespaceEntry (<root>)";
587                         else
588                                 return String.Format ("NamespaceEntry ({0},{1},{2})", FullName, IsImplicit, ID);
589                 }
590         }
591 }