2005-09-19 Chris Toshok <toshok@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 System.Collections.Specialized;
12
13 namespace Mono.CSharp {
14
15         /// <summary>
16         ///   Keeps track of the namespaces defined in the C# code.
17         ///
18         ///   This is an Expression to allow it to be referenced in the
19         ///   compiler parse/intermediate tree during name resolution.
20         /// </summary>
21         public class Namespace : FullNamedExpression {
22                 static ArrayList all_namespaces;
23                 static Hashtable namespaces_map;
24                 
25                 Namespace parent;
26                 string fullname;
27                 ArrayList entries;
28                 Hashtable namespaces;
29                 IDictionary declspaces;
30                 Hashtable cached_types;
31
32                 public readonly MemberName MemberName;
33
34                 public static Namespace Root;
35
36                 static Namespace ()
37                 {
38                         Reset ();
39                 }
40
41                 public static void Reset ()
42                 {
43                         all_namespaces = new ArrayList ();
44                         namespaces_map = new Hashtable ();
45
46                         Root = new Namespace (null, "");
47                 }
48
49                 /// <summary>
50                 ///   Constructor Takes the current namespace and the
51                 ///   name.  This is bootstrapped with parent == null
52                 ///   and name = ""
53                 /// </summary>
54                 public Namespace (Namespace parent, string name)
55                 {
56                         // Expression members.
57                         this.eclass = ExprClass.Namespace;
58                         this.Type = null;
59                         this.loc = Location.Null;
60
61                         this.parent = parent;
62
63                         string pname = parent != null ? parent.Name : "";
64                                 
65                         if (pname == "")
66                                 fullname = name;
67                         else
68                                 fullname = parent.Name + "." + name;
69
70                         if (fullname == null)
71                                 throw new InternalErrorException ("Namespace has a null fullname");
72
73                         if (parent != null && parent.MemberName != MemberName.Null)
74                                 MemberName = new MemberName (parent.MemberName, name);
75                         else if (name == "")
76                                 MemberName = MemberName.Null;
77                         else
78                                 MemberName = new MemberName (name);
79
80                         entries = new ArrayList ();
81                         namespaces = new Hashtable ();
82                         cached_types = new Hashtable ();
83
84                         all_namespaces.Add (this);
85                         if (namespaces_map.Contains (fullname))
86                                 return;
87                         namespaces_map [fullname] = true;
88                 }
89
90                 public override Expression DoResolve (EmitContext ec)
91                 {
92                         return this;
93                 }
94
95                 public override void Emit (EmitContext ec)
96                 {
97                         throw new InternalErrorException ("Expression tree referenced namespace " + fullname + " during Emit ()");
98                 }
99
100                 public static bool IsNamespace (string name)
101                 {
102                         return namespaces_map [name] != null;
103                 }
104
105                 public override string GetSignatureForError ()
106                 {
107                         return Name.Length == 0 ? "::global" : Name;
108                 }
109                 
110                 public Namespace GetNamespace (string name, bool create)
111                 {
112                         int pos = name.IndexOf ('.');
113
114                         Namespace ns;
115                         string first;
116                         if (pos >= 0)
117                                 first = name.Substring (0, pos);
118                         else
119                                 first = name;
120
121                         ns = (Namespace) namespaces [first];
122                         if (ns == null) {
123                                 if (!create)
124                                         return null;
125
126                                 ns = new Namespace (this, first);
127                                 namespaces.Add (first, ns);
128                         }
129
130                         if (pos >= 0)
131                                 ns = ns.GetNamespace (name.Substring (pos + 1), create);
132
133                         return ns;
134                 }
135
136                 public static Namespace LookupNamespace (string name, bool create)
137                 {
138                         return Root.GetNamespace (name, create);
139                 }
140
141                 TypeExpr LookupType (string name, Location loc)
142                 {
143                         if (cached_types.Contains (name))
144                                 return cached_types [name] as TypeExpr;
145
146                         Type t = null;
147                         if (declspaces != null) {
148                                 DeclSpace tdecl = declspaces [name] as DeclSpace;
149                                 if (tdecl != null) {
150                                         //
151                                         // Note that this is not:
152                                         //
153                                         //   t = tdecl.DefineType ()
154                                         //
155                                         // This is to make it somewhat more useful when a DefineType
156                                         // fails due to problems in nested types (more useful in the sense
157                                         // of fewer misleading error messages)
158                                         //
159                                         tdecl.DefineType ();
160                                         t = tdecl.TypeBuilder;
161                                 }
162                         }
163                         string lookup = t != null ? t.FullName : (fullname == "" ? name : fullname + "." + name);
164                         Type rt = TypeManager.LookupTypeReflection (lookup, loc);
165                         if (t == null)
166                                 t = rt;
167
168                         TypeExpr te = t == null ? null : new TypeExpression (t, Location.Null);
169                         cached_types [name] = te;
170                         return te;
171                 }
172
173                 public FullNamedExpression Lookup (DeclSpace ds, string name, Location loc)
174                 {
175                         Namespace ns = GetNamespace (name, false);
176                         if (ns != null)
177                                 return ns;
178
179                         TypeExpr te = LookupType (name, loc);
180                         if (te == null || !ds.CheckAccessLevel (te.Type))
181                                 return null;
182
183                         return te;
184                 }
185
186                 public void AddNamespaceEntry (NamespaceEntry entry)
187                 {
188                         entries.Add (entry);
189                 }
190
191                 public void AddDeclSpace (string name, DeclSpace ds)
192                 {
193                         if (declspaces == null)
194                                 declspaces = new HybridDictionary ();
195                         declspaces.Add (name, ds);
196                 }
197
198                 static public ArrayList UserDefinedNamespaces {
199                         get { return all_namespaces; }
200                 }
201
202                 /// <summary>
203                 ///   The qualified name of the current namespace
204                 /// </summary>
205                 public string Name {
206                         get { return fullname; }
207                 }
208
209                 public override string FullName {
210                         get { return fullname; }
211                 }
212
213                 /// <summary>
214                 ///   The parent of this namespace, used by the parser to "Pop"
215                 ///   the current namespace declaration
216                 /// </summary>
217                 public Namespace Parent {
218                         get { return parent; }
219                 }
220
221                 public static void DefineNamespaces (SymbolWriter symwriter)
222                 {
223                         foreach (Namespace ns in all_namespaces) {
224                                 foreach (NamespaceEntry entry in ns.entries)
225                                         entry.DefineNamespace (symwriter);
226                         }
227                 }
228
229                 /// <summary>
230                 ///   Used to validate that all the using clauses are correct
231                 ///   after we are finished parsing all the files.  
232                 /// </summary>
233                 public static void VerifyUsing ()
234                 {
235                         foreach (Namespace ns in all_namespaces) {
236                                 foreach (NamespaceEntry entry in ns.entries)
237                                         entry.VerifyUsing ();
238                         }
239                 }
240
241                 public override string ToString ()
242                 {
243                         if (this == Root)
244                                 return "Namespace (<root>)";
245                         else
246                                 return String.Format ("Namespace ({0})", Name);
247                 }
248         }
249
250         public class NamespaceEntry
251         {
252                 Namespace ns;
253                 NamespaceEntry parent, implicit_parent;
254                 SourceFile file;
255                 int symfile_id;
256                 Hashtable aliases;
257                 ArrayList using_clauses;
258                 public bool DeclarationFound = false;
259
260                 //
261                 // This class holds the location where a using definition is
262                 // done, and whether it has been used by the program or not.
263                 //
264                 // We use this to flag using clauses for namespaces that do not
265                 // exist.
266                 //
267                 public class UsingEntry {
268                         public readonly MemberName Name;
269                         readonly Expression Expr;
270                         readonly NamespaceEntry NamespaceEntry;
271                         readonly Location Location;
272                         
273                         public UsingEntry (NamespaceEntry entry, MemberName name, Location loc)
274                         {
275                                 Name = name;
276                                 Expr = name.GetTypeExpression ();
277                                 NamespaceEntry = entry;
278                                 Location = loc;
279                         }
280
281                         internal Namespace resolved;
282
283                         public Namespace Resolve ()
284                         {
285                                 if (resolved != null)
286                                         return resolved;
287
288                                 DeclSpace root = RootContext.Tree.Types;
289                                 root.NamespaceEntry = NamespaceEntry;
290                                 FullNamedExpression fne = Expr.ResolveAsTypeStep (root.EmitContext, false);
291                                 root.NamespaceEntry = null;
292
293                                 if (fne == null) {
294                                         Error_NamespaceNotFound (Location, Name.ToString ());
295                                         return null;
296                                 }
297
298                                 resolved = fne as Namespace;
299                                 if (resolved == null) {
300                                         Report.Error (138, Location,
301                                                 "`{0} is a type not a namespace. A using namespace directive can only be applied to namespaces", Name.ToString ());
302                                 }
303                                 return resolved;
304                         }
305                 }
306
307                 public class AliasEntry {
308                         public readonly string Name;
309                         public readonly Expression Alias;
310                         public readonly NamespaceEntry NamespaceEntry;
311                         public readonly Location Location;
312                         
313                         public AliasEntry (NamespaceEntry entry, string name, MemberName alias, Location loc)
314                         {
315                                 Name = name;
316                                 Alias = alias.GetTypeExpression ();
317                                 NamespaceEntry = entry;
318                                 Location = loc;
319                         }
320
321                         FullNamedExpression resolved;
322
323                         public FullNamedExpression Resolve ()
324                         {
325                                 if (resolved != null)
326                                         return resolved;
327
328                                 DeclSpace root = RootContext.Tree.Types;
329                                 root.NamespaceEntry = NamespaceEntry;
330                                 resolved = Alias.ResolveAsTypeStep (root.EmitContext, false);
331                                 root.NamespaceEntry = null;
332
333                                 return resolved;
334                         }
335                 }
336
337                 public NamespaceEntry (NamespaceEntry parent, SourceFile file, string name, Location loc)
338                 {
339                         this.parent = parent;
340                         this.file = file;
341                         this.IsImplicit = false;
342                         this.ID = ++next_id;
343
344                         if (parent != null)
345                                 ns = parent.NS.GetNamespace (name, true);
346                         else if (name != null)
347                                 ns = Namespace.LookupNamespace (name, true);
348                         else
349                                 ns = Namespace.Root;
350                         ns.AddNamespaceEntry (this);
351                 }
352
353
354                 private NamespaceEntry (NamespaceEntry parent, SourceFile file, Namespace ns)
355                 {
356                         this.parent = parent;
357                         this.file = file;
358                         this.IsImplicit = true;
359                         this.ID = ++next_id;
360                         this.ns = ns;
361                 }
362
363                 //
364                 // According to section 16.3.1 (using-alias-directive), the namespace-or-type-name is
365                 // resolved as if the immediately containing namespace body has no using-directives.
366                 //
367                 // Section 16.3.2 says that the same rule is applied when resolving the namespace-name
368                 // in the using-namespace-directive.
369                 //
370                 // To implement these rules, the expressions in the using directives are resolved using 
371                 // the "doppelganger" (ghostly bodiless duplicate).
372                 //
373                 NamespaceEntry doppelganger;
374                 NamespaceEntry Doppelganger {
375                         get {
376                                 if (!IsImplicit && doppelganger == null)
377                                         doppelganger = new NamespaceEntry (ImplicitParent, file, ns);
378                                 return doppelganger;
379                         }
380                 }
381
382                 static int next_id = 0;
383                 public readonly int ID;
384                 public readonly bool IsImplicit;
385
386                 public Namespace NS {
387                         get { return ns; }
388                 }
389
390                 public NamespaceEntry Parent {
391                         get { return parent; }
392                 }
393
394                 public NamespaceEntry ImplicitParent {
395                         get {
396                                 if (parent == null)
397                                         return null;
398                                 if (implicit_parent == null) {
399                                         implicit_parent = (parent.NS == ns.Parent)
400                                                 ? parent
401                                                 : new NamespaceEntry (parent, file, ns.Parent);
402                                 }
403                                 return implicit_parent;
404                         }
405                 }
406
407                 /// <summary>
408                 ///   Records a new namespace for resolving name references
409                 /// </summary>
410                 public void Using (MemberName name, Location loc)
411                 {
412                         if (DeclarationFound){
413                                 Report.Error (1529, loc, "A using clause must precede all other namespace elements except extern alias declarations");
414                                 return;
415                         }
416
417                         if (name.Equals (ns.MemberName))
418                                 return;
419                         
420                         if (using_clauses == null)
421                                 using_clauses = new ArrayList ();
422
423                         foreach (UsingEntry old_entry in using_clauses) {
424                                 if (name.Equals (old_entry.Name)) {
425                                         if (RootContext.WarningLevel >= 3)
426                                                 Report.Warning (105, loc, "The using directive for `{0}' appeared previously in this namespace", name);
427                                         return;
428                                 }
429                         }
430
431
432                         UsingEntry ue = new UsingEntry (Doppelganger, name, loc);
433                         using_clauses.Add (ue);
434                 }
435
436                 public void UsingAlias (string name, MemberName alias, Location loc)
437                 {
438                         if (DeclarationFound){
439                                 Report.Error (1529, loc, "A using clause must precede all other namespace elements except extern alias declarations");
440                                 return;
441                         }
442
443                         if (aliases == null)
444                                 aliases = new Hashtable ();
445
446                         if (aliases.Contains (name)) {
447                                 AliasEntry ae = (AliasEntry)aliases [name];
448                                 Report.SymbolRelatedToPreviousError (ae.Location, ae.Name);
449                                 Report.Error (1537, loc, "The using alias `" + name +
450                                               "' appeared previously in this namespace");
451                                 return;
452                         }
453
454                         if (RootContext.Version == LanguageVersion.Default &&
455                             name == "global" && RootContext.WarningLevel >= 2)
456                                 Report.Warning (440, loc, "An alias named `global' will not be used when resolving 'global::';" +
457                                         " the global namespace will be used instead");
458
459                         aliases [name] = new AliasEntry (Doppelganger, name, alias, loc);
460                 }
461
462                 public FullNamedExpression LookupNamespaceOrType (DeclSpace ds, string name, Location loc, bool ignore_cs0104)
463                 {
464                         // Precondition: Only simple names (no dots) will be looked up with this function.
465                         FullNamedExpression resolved = null;
466                         for (NamespaceEntry curr_ns = this; curr_ns != null; curr_ns = curr_ns.ImplicitParent) {
467                                 if ((resolved = curr_ns.Lookup (ds, name, loc, ignore_cs0104)) != null)
468                                         break;
469                         }
470                         return resolved;
471                 }
472
473                 static void Error_AmbiguousTypeReference (Location loc, string name, FullNamedExpression t1, FullNamedExpression t2)
474                 {
475                         Report.Error (104, loc, "`{0}' is an ambiguous reference between `{1}' and `{2}'",
476                                 name, t1.FullName, t2.FullName);
477                 }
478
479                 // Looks-up a alias named @name in this and surrounding namespace declarations
480                 public FullNamedExpression LookupAlias (string name)
481                 {
482                         AliasEntry entry = null;
483                         // We use Parent rather than ImplicitParent since we know implicit namespace declarations
484                         // cannot have using entries.
485                         for (NamespaceEntry n = this; n != null; n = n.Parent) {
486                                 if (n.aliases == null)
487                                         continue;
488                                 entry = n.aliases [name] as AliasEntry;
489                                 if (entry != null)
490                                         return entry.Resolve ();
491                         }
492                         return null;
493                 }
494
495                 private FullNamedExpression Lookup (DeclSpace ds, string name, Location loc, bool ignore_cs0104)
496                 {
497                         //
498                         // Check whether it's in the namespace.
499                         //
500                         FullNamedExpression fne = NS.Lookup (ds, name, loc);
501                         if (fne != null)
502                                 return fne;
503
504                         if (IsImplicit)
505                                 return null;
506
507                         //
508                         // Check aliases.
509                         //
510                         if (aliases != null) {
511                                 AliasEntry entry = aliases [name] as AliasEntry;
512                                 if (entry != null)
513                                         return entry.Resolve ();
514                         }
515
516                         //
517                         // Check using entries.
518                         //
519                         FullNamedExpression match = null;
520                         foreach (Namespace using_ns in GetUsingTable ()) {
521                                 match = using_ns.Lookup (ds, name, loc);
522                                 if (match == null || !(match is TypeExpr))
523                                         continue;
524                                 if (fne != null) {
525                                         if (!ignore_cs0104)
526                                                 Error_AmbiguousTypeReference (loc, name, fne, match);
527                                         return null;
528                                 }
529                                 fne = match;
530                         }
531
532                         return fne;
533                 }
534
535                 // Our cached computation.
536                 readonly Namespace [] empty_namespaces = new Namespace [0];
537                 Namespace [] namespace_using_table;
538                 Namespace [] GetUsingTable ()
539                 {
540                         if (namespace_using_table != null)
541                                 return namespace_using_table;
542
543                         if (using_clauses == null) {
544                                 namespace_using_table = empty_namespaces;
545                                 return namespace_using_table;
546                         }
547
548                         ArrayList list = new ArrayList (using_clauses.Count);
549
550                         foreach (UsingEntry ue in using_clauses) {
551                                 Namespace using_ns = ue.Resolve ();
552                                 if (using_ns == null)
553                                         continue;
554
555                                 list.Add (using_ns);
556                         }
557
558                         namespace_using_table = new Namespace [list.Count];
559                         list.CopyTo (namespace_using_table, 0);
560                         return namespace_using_table;
561                 }
562
563                 readonly string [] empty_using_list = new string [0];
564
565                 public void DefineNamespace (SymbolWriter symwriter)
566                 {
567                         if (symfile_id != 0)
568                                 return;
569                         if (parent != null)
570                                 parent.DefineNamespace (symwriter);
571
572                         string [] using_list = empty_using_list;
573                         if (using_clauses != null) {
574                                 using_list = new string [using_clauses.Count];
575                                 for (int i = 0; i < using_clauses.Count; i++)
576                                         using_list [i] = ((UsingEntry) using_clauses [i]).Name.ToString ();
577                         } 
578
579                         int parent_id = parent != null ? parent.symfile_id : 0;
580                         if (file.SourceFileEntry == null)
581                                 return;
582
583                         symfile_id = symwriter.DefineNamespace (
584                                 ns.Name, file.SourceFileEntry, using_list, parent_id);
585                 }
586
587                 public int SymbolFileID {
588                         get { return symfile_id; }
589                 }
590
591                 static void MsgtryRef (string s)
592                 {
593                         Console.WriteLine ("    Try using -r:" + s);
594                 }
595
596                 static void MsgtryPkg (string s)
597                 {
598                         Console.WriteLine ("    Try using -pkg:" + s);
599                 }
600
601                 public static void Error_NamespaceNotFound (Location loc, string name)
602                 {
603                         Report.Error (246, loc, "The type or namespace name `{0}' could not be found. Are you missing a using directive or an assembly reference?",
604                                 name);
605
606                         switch (name) {
607                         case "Gtk": case "GtkSharp":
608                                 MsgtryPkg ("gtk-sharp");
609                                 break;
610
611                         case "Gdk": case "GdkSharp":
612                                 MsgtryPkg ("gdk-sharp");
613                                 break;
614
615                         case "Glade": case "GladeSharp":
616                                 MsgtryPkg ("glade-sharp");
617                                 break;
618
619                         case "System.Drawing":
620                         case "System.Web.Services":
621                         case "System.Web":
622                         case "System.Data":
623                         case "System.Windows.Forms":
624                                 MsgtryRef (name);
625                                 break;
626                         }
627                 }
628
629                 /// <summary>
630                 ///   Used to validate that all the using clauses are correct
631                 ///   after we are finished parsing all the files.  
632                 /// </summary>
633                 public void VerifyUsing ()
634                 {
635                         if (using_clauses != null) {
636                                 foreach (UsingEntry ue in using_clauses)
637                                         ue.Resolve ();
638                         }
639
640                         if (aliases != null) {
641                                 foreach (DictionaryEntry de in aliases) {
642                                         AliasEntry alias = (AliasEntry) de.Value;
643                                         if (alias.Resolve () == null)
644                                                 Error_NamespaceNotFound (alias.Location, alias.Alias.ToString ());
645                                 }
646                         }
647                 }
648
649                 public string GetSignatureForError ()
650                 {
651                         if (NS == Namespace.Root)
652                                 return "::global";
653                         else
654                                 return ns.Name;
655                 }
656
657                 public override string ToString ()
658                 {
659                         if (NS == Namespace.Root)
660                                 return "NamespaceEntry (<root>)";
661                         else
662                                 return String.Format ("NamespaceEntry ({0},{1},{2})", ns.Name, IsImplicit, ID);
663                 }
664         }
665 }