2003-03-26 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[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 using Mono.Languages;
12
13 namespace Mono.CSharp {
14
15         /// <summary>
16         ///   Keeps track of the namespaces defined in the C# code.
17         /// </summary>
18         public class Namespace {
19                 static ArrayList all_namespaces = new ArrayList ();
20                 
21                 Namespace parent;
22                 string name;
23                 SourceFile file;
24                 int symfile_id;
25                 ArrayList using_clauses;
26                 Hashtable aliases;
27                 public bool DeclarationFound = false;
28
29                 //
30                 // This class holds the location where a using definition is
31                 // done, and whether it has been used by the program or not.
32                 //
33                 // We use this to flag using clauses for namespaces that do not
34                 // exist.
35                 //
36                 public class UsingEntry {
37                         public string Name;
38                         public bool Used;
39                         public Location Location;
40                         
41                         public UsingEntry (string name, Location loc)
42                         {
43                                 Name = name;
44                                 Location = loc;
45                                 Used = false;
46                         }
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, SourceFile file, string name)
55                 {
56                         this.name = name;
57                         this.file = file;
58                         this.parent = parent;
59
60                         all_namespaces.Add (this);
61                 }
62
63                 /// <summary>
64                 ///   The qualified name of the current namespace
65                 /// </summary>
66                 public string Name {
67                         get {
68                                 string pname = parent != null ? parent.Name : "";
69                                 
70                                 if (pname == "")
71                                         return name;
72                                 else
73                                         return String.Concat (parent.Name, ".", name);
74                         }
75                 }
76
77                 /// <summary>
78                 ///   The parent of this namespace, used by the parser to "Pop"
79                 ///   the current namespace declaration
80                 /// </summary>
81                 public Namespace Parent {
82                         get {
83                                 return parent;
84                         }
85                 }
86
87                 public int SymbolFileID {
88                         get {
89                                 return symfile_id;
90                         }
91                 }
92
93                 /// <summary>
94                 ///   Records a new namespace for resolving name references
95                 /// </summary>
96                 public void Using (string ns, Location loc)
97                 {
98                         if (DeclarationFound){
99                                 Report.Error (1529, loc, "A using clause must precede all other namespace elements");
100                                 return;
101                         }
102
103                         if (ns == Name)
104                                 return;
105                         
106                         if (using_clauses == null)
107                                 using_clauses = new ArrayList ();
108
109                         foreach (UsingEntry old_entry in using_clauses){
110                                 if (old_entry.Name == ns){
111                                         Report.Warning (105, loc, "The using directive for '" + ns +
112                                                         "' appeared previously in this namespace");
113                                         return;
114                                 }
115                         }
116                         
117                         UsingEntry ue = new UsingEntry (ns, loc);
118                         using_clauses.Add (ue);
119                 }
120
121                 public ArrayList UsingTable {
122                         get {
123                                 return using_clauses;
124                         }
125                 }
126
127                 public void UsingAlias (string alias, string namespace_or_type, Location loc)
128                 {
129                         if (aliases == null)
130                                 aliases = new Hashtable ();
131                         
132                         if (aliases.Contains (alias)){
133                                 Report.Error (1537, loc, "The using alias `" + alias +
134                                               "' appeared previously in this namespace");
135                                 return;
136                         }
137                                         
138                         aliases [alias] = namespace_or_type;
139                 }
140
141                 public string LookupAlias (string alias)
142                 {
143                         string value = null;
144
145                         // System.Console.WriteLine ("Lookup " + alias + " in " + name);
146
147                         if (aliases != null)
148                                 value = (string) (aliases [alias]);
149                         if (value == null && Parent != null)
150                                 value = Parent.LookupAlias (alias);
151
152                         return value;
153                 }
154
155                 void DefineNamespace (SymbolWriter symwriter)
156                 {
157                         if (symfile_id != 0)
158                                 return;
159                         if (parent != null)
160                                 parent.DefineNamespace (symwriter);
161
162                         string[] using_list;
163                         if (using_clauses != null) {
164                                 using_list = new string [using_clauses.Count];
165                                 for (int i = 0; i < using_clauses.Count; i++)
166                                         using_list [i] = ((UsingEntry) using_clauses [i]).Name;
167                         } else {
168                                 using_list = new string [0];
169                         }                               
170
171                         int parent_id = parent != null ? parent.symfile_id : 0;
172                         symfile_id = symwriter.DefineNamespace (name, file, using_list, parent_id);
173                 }
174
175                 public static void DefineNamespaces (SymbolWriter symwriter)
176                 {
177                         foreach (Namespace ns in all_namespaces)
178                                 ns.DefineNamespace (symwriter);
179                 }
180
181                 /// <summary>
182                 ///   Used to validate that all the using clauses are correct
183                 ///   after we are finished parsing all the files.  
184                 /// </summary>
185                 public static bool VerifyUsing ()
186                 {
187                         ArrayList unused = new ArrayList ();
188                         int errors = 0;
189                         
190                         foreach (Namespace ns in all_namespaces){
191                                 ArrayList uses = ns.UsingTable;
192                                 if (uses == null)
193                                         continue;
194                                 
195                                 foreach (UsingEntry ue in uses){
196                                         if (ue.Used)
197                                                 continue;
198                                         unused.Add (ue);
199                                 }
200                         }
201
202                         //
203                         // If we have unused using aliases, load all namespaces and check
204                         // whether it is unused, or it was missing
205                         //
206                         if (unused.Count > 0){
207                                 Hashtable namespaces = TypeManager.GetNamespaces ();
208
209                                 foreach (UsingEntry ue in unused){
210                                         if (namespaces.Contains (ue.Name)){
211                                                 Report.Warning (6024, ue.Location, "Unused namespace in `using' declaration");
212                                                 continue;
213                                         }
214
215                                         errors++;
216                                         Report.Error (246, ue.Location, "The namespace `" + ue.Name +
217                                                       "' can not be found (missing assembly reference?)");
218                                 }
219                         }
220                         
221                         return errors == 0;
222                 }
223
224         }
225 }