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