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