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