* codegen.cs (EmitContext.DeclSpace): Make readonly.
[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 {
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 declspaces;
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                         declspaces = 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 override string GetSignatureForError ()
103                 {
104                         return Name.Length == 0 ? "::global" : Name;
105                 }
106                 
107                 public Namespace GetNamespace (string name, bool create)
108                 {
109                         int pos = name.IndexOf ('.');
110
111                         Namespace ns;
112                         string first;
113                         if (pos >= 0)
114                                 first = name.Substring (0, pos);
115                         else
116                                 first = name;
117
118                         ns = (Namespace) namespaces [first];
119                         if (ns == null) {
120                                 if (!create)
121                                         return null;
122
123                                 ns = new Namespace (this, first);
124                                 namespaces.Add (first, ns);
125                         }
126
127                         if (pos >= 0)
128                                 ns = ns.GetNamespace (name.Substring (pos + 1), create);
129
130                         return ns;
131                 }
132
133                 public static Namespace LookupNamespace (string name, bool create)
134                 {
135                         return Root.GetNamespace (name, create);
136                 }
137
138                 TypeExpr LookupType (string name)
139                 {
140                         if (cached_types.Contains (name))
141                                 return cached_types [name] as TypeExpr;
142
143                         Type t;
144                         DeclSpace tdecl = declspaces [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                         TypeExpr te = t == null ? null : new TypeExpression (t, Location.Null);
162                         cached_types [name] = te;
163                         return te;
164                 }
165
166                 public FullNamedExpression Lookup (DeclSpace ds, string name)
167                 {
168                         Namespace ns = GetNamespace (name, false);
169                         if (ns != null)
170                                 return ns;
171
172                         TypeExpr te = LookupType (name);
173                         if (te == null || !ds.CheckAccessLevel (te.Type))
174                                 return null;
175
176                         return te;
177                 }
178
179                 public void AddNamespaceEntry (NamespaceEntry entry)
180                 {
181                         entries.Add (entry);
182                 }
183
184                 public void AddDeclSpace (string name, DeclSpace ds)
185                 {
186                         declspaces.Add (name, ds);
187                 }
188
189                 static public ArrayList UserDefinedNamespaces {
190                         get { return all_namespaces; }
191                 }
192
193                 /// <summary>
194                 ///   The qualified name of the current namespace
195                 /// </summary>
196                 public string Name {
197                         get { return fullname; }
198                 }
199
200                 public override string FullName {
201                         get { return fullname; }
202                 }
203
204                 /// <summary>
205                 ///   The parent of this namespace, used by the parser to "Pop"
206                 ///   the current namespace declaration
207                 /// </summary>
208                 public Namespace Parent {
209                         get { return parent; }
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
241         public class NamespaceEntry
242         {
243                 Namespace ns;
244                 NamespaceEntry parent, implicit_parent;
245                 SourceFile file;
246                 int symfile_id;
247                 Hashtable aliases;
248                 ArrayList using_clauses;
249                 public bool DeclarationFound = false;
250
251                 //
252                 // This class holds the location where a using definition is
253                 // done, and whether it has been used by the program or not.
254                 //
255                 // We use this to flag using clauses for namespaces that do not
256                 // exist.
257                 //
258                 public class UsingEntry {
259                         public readonly MemberName Name;
260                         readonly Expression Expr;
261                         readonly NamespaceEntry NamespaceEntry;
262                         readonly Location Location;
263                         
264                         public UsingEntry (NamespaceEntry entry, MemberName name, Location loc)
265                         {
266                                 Name = name;
267                                 Expr = name.GetTypeExpression (loc);
268                                 NamespaceEntry = entry;
269                                 Location = loc;
270                         }
271
272                         internal Namespace resolved;
273
274                         public Namespace Resolve ()
275                         {
276                                 if (resolved != null)
277                                         return resolved;
278
279                                 DeclSpace root = RootContext.Tree.Types;
280                                 root.NamespaceEntry = NamespaceEntry;
281                                 FullNamedExpression fne = Expr.ResolveAsTypeStep (root.EmitContext, false);
282                                 root.NamespaceEntry = null;
283
284                                 if (fne == null) {
285                                         Error_NamespaceNotFound (Location, Name.ToString ());
286                                         return null;
287                                 }
288
289                                 resolved = fne as Namespace;
290                                 if (resolved == null) {
291                                         Report.Error (138, Location,
292                                                 "`{0} is a type not a namespace. A using namespace directive can only be applied to namespaces", Name.ToString ());
293                                 }
294                                 return resolved;
295                         }
296                 }
297
298                 public class AliasEntry {
299                         public readonly string Name;
300                         public readonly Expression Alias;
301                         public readonly NamespaceEntry NamespaceEntry;
302                         public readonly Location Location;
303                         
304                         public AliasEntry (NamespaceEntry entry, string name, MemberName alias, Location loc)
305                         {
306                                 Name = name;
307                                 Alias = alias.GetTypeExpression (loc);
308                                 NamespaceEntry = entry;
309                                 Location = loc;
310                         }
311
312                         FullNamedExpression resolved;
313
314                         public FullNamedExpression Resolve ()
315                         {
316                                 if (resolved != null)
317                                         return resolved;
318
319                                 DeclSpace root = RootContext.Tree.Types;
320                                 root.NamespaceEntry = NamespaceEntry;
321                                 resolved = Alias.ResolveAsTypeStep (root.EmitContext, false);
322                                 root.NamespaceEntry = null;
323
324                                 return resolved;
325                         }
326                 }
327
328                 public NamespaceEntry (NamespaceEntry parent, SourceFile file, string name, Location loc)
329                 {
330                         this.parent = parent;
331                         this.file = file;
332                         this.IsImplicit = false;
333                         this.ID = ++next_id;
334
335                         if (parent != null)
336                                 ns = parent.NS.GetNamespace (name, true);
337                         else if (name != null)
338                                 ns = Namespace.LookupNamespace (name, true);
339                         else
340                                 ns = Namespace.Root;
341                         ns.AddNamespaceEntry (this);
342                 }
343
344
345                 private NamespaceEntry (NamespaceEntry parent, SourceFile file, Namespace ns)
346                 {
347                         this.parent = parent;
348                         this.file = file;
349                         this.IsImplicit = true;
350                         this.ID = ++next_id;
351                         this.ns = ns;
352                 }
353
354                 //
355                 // According to section 16.3.1 (using-alias-directive), the namespace-or-type-name is
356                 // resolved as if the immediately containing namespace body has no using-directives.
357                 //
358                 // Section 16.3.2 says that the same rule is applied when resolving the namespace-name
359                 // in the using-namespace-directive.
360                 //
361                 // To implement these rules, the expressions in the using directives are resolved using 
362                 // the "doppelganger" (ghostly bodiless duplicate).
363                 //
364                 NamespaceEntry doppelganger;
365                 NamespaceEntry Doppelganger {
366                         get {
367                                 if (!IsImplicit && doppelganger == null)
368                                         doppelganger = new NamespaceEntry (ImplicitParent, file, ns);
369                                 return doppelganger;
370                         }
371                 }
372
373                 static int next_id = 0;
374                 public readonly int ID;
375                 public readonly bool IsImplicit;
376
377                 public Namespace NS {
378                         get { return ns; }
379                 }
380
381                 public NamespaceEntry Parent {
382                         get { return parent; }
383                 }
384
385                 public NamespaceEntry ImplicitParent {
386                         get {
387                                 if (parent == null)
388                                         return null;
389                                 if (implicit_parent == null) {
390                                         implicit_parent = (parent.NS == ns.Parent)
391                                                 ? parent
392                                                 : new NamespaceEntry (parent, file, ns.Parent);
393                                 }
394                                 return implicit_parent;
395                         }
396                 }
397
398                 /// <summary>
399                 ///   Records a new namespace for resolving name references
400                 /// </summary>
401                 public void Using (MemberName name, Location loc)
402                 {
403                         if (DeclarationFound){
404                                 Report.Error (1529, loc, "A using clause must precede all other namespace elements except extern alias declarations");
405                                 return;
406                         }
407
408                         if (name.Equals (ns.MemberName))
409                                 return;
410                         
411                         if (using_clauses == null)
412                                 using_clauses = new ArrayList ();
413
414                         foreach (UsingEntry old_entry in using_clauses) {
415                                 if (name.Equals (old_entry.Name)) {
416                                         if (RootContext.WarningLevel >= 3)
417                                                 Report.Warning (105, loc, "The using directive for `{0}' appeared previously in this namespace", name);
418                                         return;
419                                 }
420                         }
421
422
423                         UsingEntry ue = new UsingEntry (Doppelganger, name, loc);
424                         using_clauses.Add (ue);
425                 }
426
427                 public void UsingAlias (string name, MemberName alias, Location loc)
428                 {
429                         if (DeclarationFound){
430                                 Report.Error (1529, loc, "A using clause must precede all other namespace elements except extern alias declarations");
431                                 return;
432                         }
433
434                         if (aliases == null)
435                                 aliases = new Hashtable ();
436
437                         if (aliases.Contains (name)) {
438                                 AliasEntry ae = (AliasEntry)aliases [name];
439                                 Report.SymbolRelatedToPreviousError (ae.Location, ae.Name);
440                                 Report.Error (1537, loc, "The using alias `" + name +
441                                               "' appeared previously in this namespace");
442                                 return;
443                         }
444
445                         aliases [name] = new AliasEntry (Doppelganger, name, alias, loc);
446                 }
447
448                 public FullNamedExpression LookupNamespaceOrType (DeclSpace ds, string name, Location loc, bool ignore_cs0104)
449                 {
450                         // Precondition: Only simple names (no dots) will be looked up with this function.
451                         FullNamedExpression resolved = null;
452                         for (NamespaceEntry curr_ns = this; curr_ns != null; curr_ns = curr_ns.ImplicitParent) {
453                                 if ((resolved = curr_ns.Lookup (ds, name, loc, ignore_cs0104)) != null)
454                                         break;
455                         }
456                         return resolved;
457                 }
458
459                 static void Error_AmbiguousTypeReference (Location loc, string name, FullNamedExpression t1, FullNamedExpression t2)
460                 {
461                         Report.Error (104, loc, "`{0}' is an ambiguous reference between `{1}' and `{2}'",
462                                 name, t1.FullName, t2.FullName);
463                 }
464
465                 private FullNamedExpression Lookup (DeclSpace ds, string name, Location loc, bool ignore_cs0104)
466                 {
467                         //
468                         // Check whether it's in the namespace.
469                         //
470                         FullNamedExpression fne = NS.Lookup (ds, name);
471                         if (fne != null)
472                                 return fne;
473
474                         if (IsImplicit)
475                                 return null;
476
477                         //
478                         // Check aliases.
479                         //
480                         if (aliases != null) {
481                                 AliasEntry entry = aliases [name] as AliasEntry;
482                                 if (entry != null)
483                                         return entry.Resolve ();
484                         }
485
486                         //
487                         // Check using entries.
488                         //
489                         FullNamedExpression match = null;
490                         foreach (Namespace using_ns in GetUsingTable ()) {
491                                 match = using_ns.Lookup (ds, name);
492                                 if (match == null || !(match is TypeExpr))
493                                         continue;
494                                 if (fne != null) {
495                                         if (!ignore_cs0104)
496                                                 Error_AmbiguousTypeReference (loc, name, fne, match);
497                                         return null;
498                                 }
499                                 fne = match;
500                         }
501
502                         return fne;
503                 }
504
505                 // Our cached computation.
506                 readonly Namespace [] empty_namespaces = new Namespace [0];
507                 Namespace [] namespace_using_table;
508                 Namespace [] GetUsingTable ()
509                 {
510                         if (namespace_using_table != null)
511                                 return namespace_using_table;
512
513                         if (using_clauses == null) {
514                                 namespace_using_table = empty_namespaces;
515                                 return namespace_using_table;
516                         }
517
518                         ArrayList list = new ArrayList (using_clauses.Count);
519
520                         foreach (UsingEntry ue in using_clauses) {
521                                 Namespace using_ns = ue.Resolve ();
522                                 if (using_ns == null)
523                                         continue;
524
525                                 list.Add (using_ns);
526                         }
527
528                         namespace_using_table = new Namespace [list.Count];
529                         list.CopyTo (namespace_using_table, 0);
530                         return namespace_using_table;
531                 }
532
533                 readonly string [] empty_using_list = new string [0];
534
535                 public void DefineNamespace (SymbolWriter symwriter)
536                 {
537                         if (symfile_id != 0)
538                                 return;
539                         if (parent != null)
540                                 parent.DefineNamespace (symwriter);
541
542                         string [] using_list = empty_using_list;
543                         if (using_clauses != null) {
544                                 using_list = new string [using_clauses.Count];
545                                 for (int i = 0; i < using_clauses.Count; i++)
546                                         using_list [i] = ((UsingEntry) using_clauses [i]).Name.ToString ();
547                         } 
548
549                         int parent_id = parent != null ? parent.symfile_id : 0;
550                         if (file.SourceFileEntry == null)
551                                 return;
552
553                         symfile_id = symwriter.DefineNamespace (
554                                 ns.Name, file.SourceFileEntry, using_list, parent_id);
555                 }
556
557                 public int SymbolFileID {
558                         get { return symfile_id; }
559                 }
560
561                 static void MsgtryRef (string s)
562                 {
563                         Console.WriteLine ("    Try using -r:" + s);
564                 }
565
566                 static void MsgtryPkg (string s)
567                 {
568                         Console.WriteLine ("    Try using -pkg:" + s);
569                 }
570
571                 public static void Error_NamespaceNotFound (Location loc, string name)
572                 {
573                         Report.Error (246, loc, "The type or namespace name `{0}' could not be found. Are you missing a using directive or an assembly reference?",
574                                 name);
575
576                         switch (name) {
577                         case "Gtk": case "GtkSharp":
578                                 MsgtryPkg ("gtk-sharp");
579                                 break;
580
581                         case "Gdk": case "GdkSharp":
582                                 MsgtryPkg ("gdk-sharp");
583                                 break;
584
585                         case "Glade": case "GladeSharp":
586                                 MsgtryPkg ("glade-sharp");
587                                 break;
588
589                         case "System.Drawing":
590                         case "System.Web.Services":
591                         case "System.Web":
592                         case "System.Data":
593                         case "System.Windows.Forms":
594                                 MsgtryRef (name);
595                                 break;
596                         }
597                 }
598
599                 /// <summary>
600                 ///   Used to validate that all the using clauses are correct
601                 ///   after we are finished parsing all the files.  
602                 /// </summary>
603                 public void VerifyUsing ()
604                 {
605                         if (using_clauses != null) {
606                                 foreach (UsingEntry ue in using_clauses)
607                                         ue.Resolve ();
608                         }
609
610                         if (aliases != null) {
611                                 foreach (DictionaryEntry de in aliases) {
612                                         AliasEntry alias = (AliasEntry) de.Value;
613                                         if (alias.Resolve () == null)
614                                                 Error_NamespaceNotFound (alias.Location, alias.Alias.ToString ());
615                                 }
616                         }
617                 }
618
619                 public override string ToString ()
620                 {
621                         if (NS == Namespace.Root)
622                                 return "NamespaceEntry (<root>)";
623                         else
624                                 return String.Format ("NamespaceEntry ({0},{1},{2})", ns.Name, IsImplicit, ID);
625                 }
626         }
627 }