Copied remotely
[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 : IAlias {
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 IAlias Lookup (DeclSpace ds, string name, Location loc)
92                 {
93                         IAlias o = Lookup (name);
94
95                         Type t;
96                         DeclSpace tdecl = o as DeclSpace;
97                         if (tdecl != null) {
98                                 t = tdecl.DefineType ();
99                                 if (t == null)
100                                         return null;
101
102                                 if ((ds == null) || ds.CheckAccessLevel (t))
103                                         return new TypeExpression (t, loc);
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 new TypeExpression (t, loc);
115                 }
116
117                 public void AddNamespaceEntry (NamespaceEntry entry)
118                 {
119                         entries.Add (entry);
120                 }
121
122                 public void DefineName (string name, IAlias o)
123                 {
124                         defined_names.Add (name, o);
125                 }
126
127                 public IAlias Lookup (string name)
128                 {
129                         return (IAlias) 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                 bool IAlias.IsType {
186                         get { return false; }
187                 }
188
189                 TypeExpr IAlias.ResolveAsType (EmitContext ec)
190                 {
191                         throw new InvalidOperationException ();
192                 }
193         }
194
195         public class NamespaceEntry
196         {
197                 Namespace ns;
198                 NamespaceEntry parent, implicit_parent;
199                 SourceFile file;
200                 int symfile_id;
201                 Hashtable aliases;
202                 ArrayList using_clauses;
203                 public bool DeclarationFound = false;
204
205                 //
206                 // This class holds the location where a using definition is
207                 // done, and whether it has been used by the program or not.
208                 //
209                 // We use this to flag using clauses for namespaces that do not
210                 // exist.
211                 //
212                 public class UsingEntry {
213                         public readonly string Name;
214                         public readonly NamespaceEntry NamespaceEntry;
215                         public readonly Location Location;
216                         
217                         public UsingEntry (NamespaceEntry entry, string name, Location loc)
218                         {
219                                 Name = name;
220                                 NamespaceEntry = entry;
221                                 Location = loc;
222                         }
223
224                         Namespace resolved_ns;
225
226                         public Namespace Resolve ()
227                         {
228                                 if (resolved_ns != null)
229                                         return resolved_ns;
230
231                                 object resolved = NamespaceEntry.LookupForUsing (Name, Location);
232                                 resolved_ns = resolved as Namespace;
233                                 return resolved_ns;
234                         }
235                 }
236
237                 public class AliasEntry {
238                         public readonly string Name;
239                         public readonly MemberName Alias;
240                         public readonly NamespaceEntry NamespaceEntry;
241                         public readonly Location Location;
242                         
243                         public AliasEntry (NamespaceEntry entry, string name, MemberName alias, Location loc)
244                         {
245                                 Name = name;
246                                 Alias = alias;
247                                 NamespaceEntry = entry;
248                                 Location = loc;
249                         }
250
251                         IAlias resolved;
252
253                         public IAlias Resolve ()
254                         {
255                                 if (resolved != null)
256                                         return resolved;
257
258                                 //
259                                 // GENERICS: Cope with the expression and not with the string
260                                 // this will fail with `using A = Stack<int>'
261                                 //
262                                 
263                                 string alias = Alias.GetTypeName ();
264
265                                 resolved = NamespaceEntry.LookupForUsing (alias, Location);
266                                 if (resolved == null)
267                                         return null;
268
269                                 if (resolved.IsType)
270                                         resolved = new TypeAliasExpression (
271                                                 resolved, Alias.TypeArguments, Location);
272
273                                 return resolved;
274                         }
275                 }
276
277                 public NamespaceEntry (NamespaceEntry parent, SourceFile file, string name, Location loc)
278                         : this (parent, file, name, false, loc)
279                 { }
280
281                 protected NamespaceEntry (NamespaceEntry parent, SourceFile file, string name, bool is_implicit, Location loc)
282                 {
283                         this.parent = parent;
284                         this.file = file;
285                         this.IsImplicit = is_implicit;
286                         this.ID = ++next_id;
287
288                         if (!is_implicit && (parent != null))
289                                 ns = parent.NS.GetNamespace (name, true);
290                         else if (name != null)
291                                 ns = Namespace.LookupNamespace (name, true);
292                         else
293                                 ns = Namespace.Root;
294                         ns.AddNamespaceEntry (this);
295
296                         if ((parent != null) && (parent.NS != ns.Parent))
297                                 implicit_parent = new NamespaceEntry (parent, file, ns.Parent.Name, true, loc);
298                         else
299                                 implicit_parent = parent;
300
301                         this.FullName = ns.Name;
302                 }
303
304                 static int next_id = 0;
305                 public readonly string FullName;
306                 public readonly int ID;
307                 public readonly bool IsImplicit;
308
309                 public Namespace NS {
310                         get {
311                                 return ns;
312                         }
313                 }
314
315                 public NamespaceEntry Parent {
316                         get {
317                                 return parent;
318                         }
319                 }
320
321                 public NamespaceEntry ImplicitParent {
322                         get {
323                                 return implicit_parent;
324                         }
325                 }
326
327                 public void DefineName (string name, IAlias o)
328                 {
329                         ns.DefineName (name, o);
330                 }
331
332                 /// <summary>
333                 ///   Records a new namespace for resolving name references
334                 /// </summary>
335                 public void Using (string ns, Location loc)
336                 {
337                         if (DeclarationFound){
338                                 Report.Error (1529, loc, "A using clause must precede all other namespace elements");
339                                 return;
340                         }
341
342                         if (ns == FullName)
343                                 return;
344                         
345                         if (using_clauses == null)
346                                 using_clauses = new ArrayList ();
347
348                         foreach (UsingEntry old_entry in using_clauses) {
349                                 if (old_entry.Name == ns) {
350                                         if (RootContext.WarningLevel >= 3)
351                                                 Report.Warning (105, loc, "The using directive for '{0}' appeared previously in this namespace", ns);
352                                                 return;
353                                         }
354                                 }
355                         
356                         UsingEntry ue = new UsingEntry (this, ns, loc);
357                         using_clauses.Add (ue);
358                 }
359
360                 public void UsingAlias (string name, MemberName alias, Location loc)
361                 {
362                         if (DeclarationFound){
363                                 Report.Error (1529, loc, "A using clause must precede all other namespace elements");
364                                 return;
365                         }
366
367                         if (aliases == null)
368                                 aliases = new Hashtable ();
369
370                         if (aliases.Contains (name)){
371                                 Report.Error (1537, loc, "The using alias `" + name +
372                                               "' appeared previously in this namespace");
373                                 return;
374                         }
375
376                         aliases [name] = new AliasEntry (this, name, alias, loc);
377                 }
378
379                 protected AliasEntry GetAliasEntry (string alias)
380                 {
381                         AliasEntry entry = null;
382
383                         if (aliases != null)
384                                 entry = (AliasEntry) aliases [alias];
385                         if (entry == null && Parent != null)
386                                 entry = Parent.GetAliasEntry (alias);
387
388                         return entry;
389                 }
390
391                 public IAlias LookupAlias (string alias)
392                 {
393                         AliasEntry entry = GetAliasEntry (alias);
394
395                         if (entry == null)
396                                 return null;
397
398                         return entry.Resolve ();
399                 }
400
401                 //
402                 // According to section 16.3.1 (using-alias-directive), the namespace-or-type-name is 
403                 // resolved as if the immediately containing namespace body has no using-directives.
404                 //
405                 // Section 16.3.2 says that the same rule is applied when resolving the namespace-name
406                 // in the using-namespace-directive.
407                 //
408                 public IAlias LookupForUsing (string dotted_name, Location loc)
409                 {
410                         int pos = dotted_name.IndexOf ('.');
411                         string simple_name = dotted_name;
412                         string rest = null;
413                         if (pos >= 0) {
414                                 simple_name = dotted_name.Substring (0, pos);
415                                 rest = dotted_name.Substring (pos + 1);
416                         }
417
418                         IAlias o = NS.Lookup (null, simple_name, loc);
419                         if (o == null && ImplicitParent != null)
420                                 o = ImplicitParent.LookupNamespaceOrType (null, simple_name, loc);
421
422                         if (o == null || rest == null)
423                                 return o;
424
425                         Namespace ns = o as Namespace;
426                         if (ns != null)
427                                 return ns.Lookup (null, rest, loc);
428                         
429                         Type nested = TypeManager.LookupType (o.Name + "." + rest);
430                         if (nested == null)
431                                 return null;
432
433                         return new TypeExpression (nested, loc);
434                 }
435
436                 public IAlias LookupNamespaceOrType (DeclSpace ds, string name, Location loc)
437                 {
438                         IAlias resolved = null;
439                         for (NamespaceEntry curr_ns = this; curr_ns != null; curr_ns = curr_ns.ImplicitParent) {
440                                 if ((resolved = curr_ns.Lookup (ds, name, loc)) != null)
441                                         break;
442                         }
443                         return resolved;
444                 }
445
446                 private IAlias Lookup (DeclSpace ds, string name, Location loc)
447                 {
448                         IAlias o;
449                         Namespace ns;
450
451                         //
452                         // If name is of the form `N.I', first lookup `N', then search a member `I' in it.
453                         //
454                         // FIXME: Remove this block.  Only simple names should come here.
455                         //        The bug: The loop in LookupNamespaceOrType continues if 
456                         //        the lookup for N succeeds but the nested lookup for I fails.
457                         //        This is one part of #52697.
458                         //
459                         int pos = name.IndexOf ('.');
460                         if (pos >= 0) {
461                                 string first = name.Substring (0, pos);
462                                 string last = name.Substring (pos + 1);
463
464                                 o = Lookup (ds, first, loc);
465                                 if (o == null)
466                                         return null;
467
468                                 ns = o as Namespace;
469                                 if (ns != null) {
470                                         o = ns.Lookup (ds, last, loc);
471                                         return o;
472                                 }
473
474                                 Type nested = TypeManager.LookupType (o.Name + "." + last);
475                                 if ((nested == null) || ((ds != null) && !ds.CheckAccessLevel (nested)))
476                                         return null;
477
478                                 return new TypeExpression (nested, loc);
479                         }
480
481                         //
482                         // Check whether it's in the namespace.
483                         //
484                         o = NS.Lookup (ds, name, loc);
485                         if (o != null)
486                                 return o;
487
488                         //
489                         // Check aliases.
490                         //
491                         AliasEntry entry = GetAliasEntry (name);
492                         if (entry != null) {
493                                 IAlias alias = entry.Resolve ();
494                                 if (alias != null)
495                                         return alias;
496                         }
497
498                         if (name.IndexOf ('.') > 0)
499                                 return null;
500
501                         //
502                         // Check using entries.
503                         //
504                         IAlias t = null, match = null;
505                         foreach (Namespace using_ns in GetUsingTable ()) {
506                                 match = using_ns.Lookup (ds, name, loc);
507                                 if ((match != null) && match.IsType){
508                                         if (t != null) {
509                                                         DeclSpace.Error_AmbiguousTypeReference (loc, name, t.Name, match.Name);
510                                                 return null;
511                                         } else {
512                                                 t = match;
513                                         }
514                                 }
515                         }
516
517                         return t;
518                 }
519
520                 // Our cached computation.
521                 Namespace [] namespace_using_table;
522                 public Namespace[] GetUsingTable ()
523                 {
524                         if (namespace_using_table != null)
525                                 return namespace_using_table;
526                         
527                         if (using_clauses == null)
528                                 return new Namespace [0];
529
530                         ArrayList list = new ArrayList (using_clauses.Count);
531
532                         foreach (UsingEntry ue in using_clauses) {
533                                 Namespace using_ns = ue.Resolve ();
534                                 if (using_ns == null)
535                                         continue;
536
537                                 list.Add (using_ns);
538                         }
539
540                         namespace_using_table = new Namespace [list.Count];
541                         list.CopyTo (namespace_using_table, 0);
542                         return namespace_using_table;
543                 }
544
545                 public void DefineNamespace (SymbolWriter symwriter)
546                 {
547                         if (symfile_id != 0)
548                                 return;
549                         if (parent != null)
550                                 parent.DefineNamespace (symwriter);
551
552                         string[] using_list;
553                         if (using_clauses != null) {
554                                 using_list = new string [using_clauses.Count];
555                                 for (int i = 0; i < using_clauses.Count; i++)
556                                         using_list [i] = ((UsingEntry) using_clauses [i]).Name;
557                         } else {
558                                 using_list = new string [0];
559                         }
560
561                         int parent_id = parent != null ? parent.symfile_id : 0;
562                         if (file.SourceFileEntry == null)
563                                 return;
564
565                         symfile_id = symwriter.DefineNamespace (
566                                 ns.Name, file.SourceFileEntry, using_list, parent_id);
567                 }
568
569                 public int SymbolFileID {
570                         get {
571                                 return symfile_id;
572                         }
573                 }
574
575                 static void MsgtryRef (string s)
576                 {
577                         Console.WriteLine ("    Try using -r:" + s);
578                 }
579                 
580                 static void MsgtryPkg (string s)
581                 {
582                         Console.WriteLine ("    Try using -pkg:" + s);
583                 }
584
585                 protected void error246 (Location loc, string name)
586                 {
587                         Report.Error (246, loc, "The namespace `" + name +
588                                       "' can not be found (missing assembly reference?)");
589
590                         switch (name) {
591                         case "Gtk": case "GtkSharp":
592                                 MsgtryPkg ("gtk-sharp");
593                                 break;
594
595                         case "Gdk": case "GdkSharp":
596                                 MsgtryPkg ("gdk-sharp");
597                                 break;
598
599                         case "Glade": case "GladeSharp":
600                                 MsgtryPkg ("glade-sharp");
601                                 break;
602
603                         case "System.Drawing":
604                         case "System.Web.Services":
605                         case "System.Web":
606                         case "System.Data":
607                         case "System.Windows.Forms":
608                                 MsgtryRef (name);
609                                 break;
610                         }
611                 }
612
613                 /// <summary>
614                 ///   Used to validate that all the using clauses are correct
615                 ///   after we are finished parsing all the files.  
616                 /// </summary>
617                 public void VerifyUsing ()
618                 {
619                         TypeContainer dummy = new RootTypes ();
620                         EmitContext ec = new EmitContext (
621                                 dummy, Location.Null, null, null, 0, false);
622
623                         if (using_clauses != null){
624                                 foreach (UsingEntry ue in using_clauses){
625                                         if (ue.Resolve () != null)
626                                                 continue;
627
628                                         if (LookupForUsing (ue.Name, ue.Location) == null)
629                                                 error246 (ue.Location, ue.Name);
630                                         else
631                                                 Report.Error (138, ue.Location, "The using keyword only lets you specify a namespace, " +
632                                                               "`" + ue.Name + "' is a class not a namespace.");
633
634                                 }
635                         }
636
637                         if (aliases != null){
638                                 foreach (DictionaryEntry de in aliases){
639                                         AliasEntry entry = (AliasEntry) de.Value;
640
641                                         IAlias alias = entry.Resolve ();
642                                         if (alias != null) {
643                                                 if (alias.IsType)
644                                                         alias.ResolveAsType (ec);
645
646                                                 continue;
647                                         }
648
649                                         error246 (entry.Location, entry.Alias.GetTypeName ());
650                                 }
651                         }
652                 }
653
654                 public override string ToString ()
655                 {
656                         if (NS == Namespace.Root)
657                                 return "NamespaceEntry (<root>)";
658                         else
659                                 return String.Format ("NamespaceEntry ({0},{1},{2})", FullName, IsImplicit, ID);
660                 }
661         }
662 }