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