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