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