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