// // namespace.cs: Tracks namespaces // // Author: // Miguel de Icaza (miguel@ximian.com) // Marek Safar (marek.safar@seznam.cz) // // Copyright 2001 Ximian, Inc. // Copyright 2003-2008 Novell, Inc. // Copyright 2011 Xamarin Inc // using System; using System.Collections.Generic; using System.Linq; using Mono.CompilerServices.SymbolWriter; namespace Mono.CSharp { public class RootNamespace : Namespace { readonly string alias_name; readonly Dictionary all_namespaces; public RootNamespace (string alias_name) : base () { this.alias_name = alias_name; RegisterNamespace (this); all_namespaces = new Dictionary (); all_namespaces.Add ("", this); } public string Alias { get { return alias_name; } } public static void Error_GlobalNamespaceRedefined (Report report, Location loc) { report.Error (1681, loc, "The global extern alias cannot be redefined"); } // // For better error reporting where we try to guess missing using directive // public List FindTypeNamespaces (IMemberContext ctx, string name, int arity) { List res = null; foreach (var ns in all_namespaces) { var type = ns.Value.LookupType (ctx, name, arity, LookupMode.Normal, Location.Null); if (type != null) { if (res == null) res = new List (); res.Add (ns.Key); } } return res; } // // For better error reporting where compiler tries to guess missing using directive // public List FindExtensionMethodNamespaces (IMemberContext ctx, string name, int arity) { List res = null; foreach (var ns in all_namespaces) { if (ns.Key.Length == 0) continue; var methods = ns.Value.LookupExtensionMethod (ctx, name, arity); if (methods != null) { if (res == null) res = new List (); res.Add (ns.Key); } } return res; } public void RegisterNamespace (Namespace child) { if (child != this) all_namespaces.Add (child.Name, child); } public override string GetSignatureForError () { return alias_name + "::"; } } public sealed class GlobalRootNamespace : RootNamespace { public GlobalRootNamespace () : base ("global") { } } // // Namespace cache for imported and compiled namespaces // public class Namespace { readonly Namespace parent; string fullname; protected Dictionary namespaces; protected Dictionary> types; List extension_method_types; Dictionary cached_types; bool cls_checked; /// /// Constructor Takes the current namespace and the /// name. This is bootstrapped with parent == null /// and name = "" /// public Namespace (Namespace parent, string name) : this () { if (name == null) throw new ArgumentNullException ("name"); this.parent = parent; string pname = parent != null ? parent.fullname : null; if (pname == null) fullname = name; else fullname = pname + "." + name; while (parent.parent != null) parent = parent.parent; var root = parent as RootNamespace; if (root == null) throw new InternalErrorException ("Root namespaces must be created using RootNamespace"); root.RegisterNamespace (this); } protected Namespace () { namespaces = new Dictionary (); cached_types = new Dictionary (); } #region Properties /// /// The qualified name of the current namespace /// public string Name { get { return fullname; } } /// /// The parent of this namespace, used by the parser to "Pop" /// the current namespace declaration /// public Namespace Parent { get { return parent; } } #endregion public Namespace AddNamespace (MemberName name) { var ns_parent = name.Left == null ? this : AddNamespace (name.Left); return ns_parent.TryAddNamespace (name.Basename); } Namespace TryAddNamespace (string name) { Namespace ns; if (!namespaces.TryGetValue (name, out ns)) { ns = new Namespace (this, name); namespaces.Add (name, ns); } return ns; } public bool TryGetNamespace (string name, out Namespace ns) { return namespaces.TryGetValue (name, out ns); } // TODO: Replace with CreateNamespace where MemberName is created for the method call public Namespace GetNamespace (string name, bool create) { int pos = name.IndexOf ('.'); Namespace ns; string first; if (pos >= 0) first = name.Substring (0, pos); else first = name; if (!namespaces.TryGetValue (first, out ns)) { if (!create) return null; ns = new Namespace (this, first); namespaces.Add (first, ns); } if (pos >= 0) ns = ns.GetNamespace (name.Substring (pos + 1), create); return ns; } public IList GetAllTypes (string name) { IList found; if (types == null || !types.TryGetValue (name, out found)) return null; return found; } public virtual string GetSignatureForError () { return fullname; } public TypeSpec LookupType (IMemberContext ctx, string name, int arity, LookupMode mode, Location loc) { if (types == null) return null; TypeSpec best = null; if (arity == 0 && cached_types.TryGetValue (name, out best)) { if (best != null || mode != LookupMode.IgnoreAccessibility) return best; } IList found; if (!types.TryGetValue (name, out found)) return null; foreach (var ts in found) { if (ts.Arity == arity) { if (best == null) { if ((ts.Modifiers & Modifiers.INTERNAL) != 0 && !ts.MemberDefinition.IsInternalAsPublic (ctx.Module.DeclaringAssembly) && mode != LookupMode.IgnoreAccessibility) continue; best = ts; continue; } if (best.MemberDefinition.IsImported && ts.MemberDefinition.IsImported) { if (ts.Kind == MemberKind.MissingType) continue; if (best.Kind == MemberKind.MissingType) { best = ts; continue; } if (mode == LookupMode.Normal) { ctx.Module.Compiler.Report.SymbolRelatedToPreviousError (best); ctx.Module.Compiler.Report.SymbolRelatedToPreviousError (ts); ctx.Module.Compiler.Report.Error (433, loc, "The imported type `{0}' is defined multiple times", ts.GetSignatureForError ()); } break; } if (ts.Kind == MemberKind.MissingType) continue; if (best.MemberDefinition.IsImported) best = ts; if ((best.Modifiers & Modifiers.INTERNAL) != 0 && !best.MemberDefinition.IsInternalAsPublic (ctx.Module.DeclaringAssembly)) continue; if (mode != LookupMode.Normal) continue; if (ts.MemberDefinition.IsImported) { ctx.Module.Compiler.Report.SymbolRelatedToPreviousError (best); ctx.Module.Compiler.Report.SymbolRelatedToPreviousError (ts); } ctx.Module.Compiler.Report.Warning (436, 2, loc, "The type `{0}' conflicts with the imported type of same name'. Ignoring the imported type definition", best.GetSignatureForError ()); } // // Lookup for the best candidate with the closest arity match // if (arity < 0) { if (best == null) { best = ts; } else if (System.Math.Abs (ts.Arity + arity) < System.Math.Abs (best.Arity + arity)) { best = ts; } } } // TODO MemberCache: Cache more if (arity == 0 && mode == LookupMode.Normal) cached_types.Add (name, best); if (best != null) { var dep = best.GetMissingDependencies (); if (dep != null) ImportedTypeDefinition.Error_MissingDependency (ctx, dep, loc); } return best; } public FullNamedExpression LookupTypeOrNamespace (IMemberContext ctx, string name, int arity, LookupMode mode, Location loc) { var texpr = LookupType (ctx, name, arity, mode, loc); Namespace ns; if (arity == 0 && namespaces.TryGetValue (name, out ns)) { if (texpr == null) return new NamespaceExpression (ns, loc); if (mode != LookupMode.Probing) { //ctx.Module.Compiler.Report.SymbolRelatedToPreviousError (texpr.Type); // ctx.Module.Compiler.Report.SymbolRelatedToPreviousError (ns.loc, ""); ctx.Module.Compiler.Report.Warning (437, 2, loc, "The type `{0}' conflicts with the imported namespace `{1}'. Using the definition found in the source file", texpr.GetSignatureForError (), ns.GetSignatureForError ()); } if (texpr.MemberDefinition.IsImported) return new NamespaceExpression (ns, loc); } if (texpr == null) return null; return new TypeExpression (texpr, loc); } // // Completes types with the given `prefix' // public IEnumerable CompletionGetTypesStartingWith (string prefix) { if (types == null) return Enumerable.Empty (); var res = from item in types where item.Key.StartsWith (prefix) && item.Value.Any (l => (l.Modifiers & Modifiers.PUBLIC) != 0) select item.Key; if (namespaces != null) res = res.Concat (from item in namespaces where item.Key.StartsWith (prefix) select item.Key); return res; } // // Looks for extension method in this namespace // public List LookupExtensionMethod (IMemberContext invocationContext, string name, int arity) { if (extension_method_types == null) return null; List found = null; for (int i = 0; i < extension_method_types.Count; ++i) { var ts = extension_method_types[i]; // // When the list was built we didn't know what members the type // contains // if ((ts.Modifiers & Modifiers.METHOD_EXTENSION) == 0) { if (extension_method_types.Count == 1) { extension_method_types = null; return found; } extension_method_types.RemoveAt (i--); continue; } var res = ts.MemberCache.FindExtensionMethods (invocationContext, name, arity); if (res == null) continue; if (found == null) { found = res; } else { found.AddRange (res); } } return found; } public void AddType (ModuleContainer module, TypeSpec ts) { if (types == null) { types = new Dictionary> (64); } if (ts.IsClass && ts.Arity == 0) { var extension_method_allowed = ts.MemberDefinition.IsImported ? (ts.Modifiers & Modifiers.METHOD_EXTENSION) != 0 : (ts.IsStatic || ts.MemberDefinition.IsPartial); if (extension_method_allowed) { if (extension_method_types == null) extension_method_types = new List (); extension_method_types.Add (ts); } } var name = ts.Name; IList existing; if (types.TryGetValue (name, out existing)) { TypeSpec better_type; TypeSpec found; if (existing.Count == 1) { found = existing[0]; if (ts.Arity == found.Arity) { better_type = IsImportedTypeOverride (module, ts, found); if (better_type == found) return; if (better_type != null) { existing [0] = better_type; return; } } existing = new List (); existing.Add (found); types[name] = existing; } else { for (int i = 0; i < existing.Count; ++i) { found = existing[i]; if (ts.Arity != found.Arity) continue; better_type = IsImportedTypeOverride (module, ts, found); if (better_type == found) return; if (better_type != null) { existing.RemoveAt (i); --i; continue; } } } existing.Add (ts); } else { types.Add (name, new TypeSpec[] { ts }); } } // // We import any types but in the situation there are same types // but one has better visibility (either public or internal with friend) // the less visible type is removed from the namespace cache // public static TypeSpec IsImportedTypeOverride (ModuleContainer module, TypeSpec ts, TypeSpec found) { var ts_accessible = (ts.Modifiers & Modifiers.PUBLIC) != 0 || ts.MemberDefinition.IsInternalAsPublic (module.DeclaringAssembly); var found_accessible = (found.Modifiers & Modifiers.PUBLIC) != 0 || found.MemberDefinition.IsInternalAsPublic (module.DeclaringAssembly); if (ts_accessible && !found_accessible) return ts; // found is better always better for accessible or inaccessible ts if (!ts_accessible) return found; return null; } public void RemoveContainer (TypeContainer tc) { IList found; if (types.TryGetValue (tc.MemberName.Name, out found)) { for (int i = 0; i < found.Count; ++i) { if (tc.MemberName.Arity != found [i].Arity) continue; if (found.Count == 1) types.Remove (tc.MemberName.Name); else found.RemoveAt (i); break; } } cached_types.Remove (tc.MemberName.Basename); } public void SetBuiltinType (BuiltinTypeSpec pts) { var found = types[pts.Name]; cached_types.Remove (pts.Name); if (found.Count == 1) { types[pts.Name][0] = pts; } else { throw new NotImplementedException (); } } public void VerifyClsCompliance () { if (types == null || cls_checked) return; cls_checked = true; // TODO: This is quite ugly way to check for CLS compliance at namespace level var locase_types = new Dictionary> (StringComparer.OrdinalIgnoreCase); foreach (var tgroup in types.Values) { foreach (var tm in tgroup) { if ((tm.Modifiers & Modifiers.PUBLIC) == 0 || !tm.IsCLSCompliant ()) continue; List found; if (!locase_types.TryGetValue (tm.Name, out found)) { found = new List (); locase_types.Add (tm.Name, found); } found.Add (tm); } } foreach (var locase in locase_types.Values) { if (locase.Count < 2) continue; bool all_same = true; foreach (var notcompliant in locase) { all_same = notcompliant.Name == locase[0].Name; if (!all_same) break; } if (all_same) continue; TypeContainer compiled = null; foreach (var notcompliant in locase) { if (!notcompliant.MemberDefinition.IsImported) { if (compiled != null) compiled.Compiler.Report.SymbolRelatedToPreviousError (compiled); compiled = notcompliant.MemberDefinition as TypeContainer; } else { compiled.Compiler.Report.SymbolRelatedToPreviousError (notcompliant); } } compiled.Compiler.Report.Warning (3005, 1, compiled.Location, "Identifier `{0}' differing only in case is not CLS-compliant", compiled.GetSignatureForError ()); } } } public class CompilationSourceFile : NamespaceContainer { readonly SourceFile file; CompileUnitEntry comp_unit; Dictionary include_files; Dictionary conditionals; public CompilationSourceFile (ModuleContainer parent, SourceFile sourceFile) : this (parent) { this.file = sourceFile; } public CompilationSourceFile (ModuleContainer parent) : base (parent) { } public CompileUnitEntry SymbolUnitEntry { get { return comp_unit; } } public string FileName { get { return file.Name; } } public SourceFile SourceFile { get { return file; } } public void AddIncludeFile (SourceFile file) { if (file == this.file) return; if (include_files == null) include_files = new Dictionary (); if (!include_files.ContainsKey (file.OriginalFullPathName)) include_files.Add (file.OriginalFullPathName, file); } public void AddDefine (string value) { if (conditionals == null) conditionals = new Dictionary (2); conditionals[value] = true; } public void AddUndefine (string value) { if (conditionals == null) conditionals = new Dictionary (2); conditionals[value] = false; } public override void PrepareEmit () { var sw = Module.DeclaringAssembly.SymbolWriter; if (sw != null) { CreateUnitSymbolInfo (sw, Compiler.Settings.PathMap); } base.PrepareEmit (); } // // Creates symbol file index in debug symbol file // void CreateUnitSymbolInfo (MonoSymbolFile symwriter, List> pathMap) { var si = file.CreateSymbolInfo (symwriter, pathMap); comp_unit = new CompileUnitEntry (symwriter, si); if (include_files != null) { foreach (SourceFile include in include_files.Values) { si = include.CreateSymbolInfo (symwriter, pathMap); comp_unit.AddFile (si); } } } public bool IsConditionalDefined (string value) { if (conditionals != null) { bool res; if (conditionals.TryGetValue (value, out res)) return res; // When conditional was undefined if (conditionals.ContainsKey (value)) return false; } return Compiler.Settings.IsConditionalSymbolDefined (value); } public override void Accept (StructuralVisitor visitor) { visitor.Visit (this); } } // // Namespace block as created by the parser // public class NamespaceContainer : TypeContainer, IMemberContext { static readonly Namespace[] empty_namespaces = new Namespace[0]; readonly Namespace ns; public new readonly NamespaceContainer Parent; List clauses; // Used by parsed to check for parser errors public bool DeclarationFound; Namespace[] namespace_using_table; TypeSpec[] types_using_table; Dictionary aliases; public NamespaceContainer (MemberName name, NamespaceContainer parent) : base (parent, name, null, MemberKind.Namespace) { this.Parent = parent; this.ns = parent.NS.AddNamespace (name); containers = new List (); } protected NamespaceContainer (ModuleContainer parent) : base (parent, null, null, MemberKind.Namespace) { ns = parent.GlobalRootNamespace; containers = new List (2); } #region Properties public override AttributeTargets AttributeTargets { get { throw new NotSupportedException (); } } public override string DocCommentHeader { get { throw new NotSupportedException (); } } public Namespace NS { get { return ns; } } public List Usings { get { return clauses; } } public override string[] ValidAttributeTargets { get { throw new NotSupportedException (); } } #endregion public void AddUsing (UsingClause un) { if (DeclarationFound){ Compiler.Report.Error (1529, un.Location, "A using clause must precede all other namespace elements except extern alias declarations"); } if (clauses == null) clauses = new List (); clauses.Add (un); } public void AddUsing (UsingAliasNamespace un) { if (DeclarationFound){ Compiler.Report.Error (1529, un.Location, "A using clause must precede all other namespace elements except extern alias declarations"); } AddAlias (un); } void AddAlias (UsingAliasNamespace un) { if (clauses == null) { clauses = new List (); } else { foreach (var entry in clauses) { var a = entry as UsingAliasNamespace; if (a != null && a.Alias.Value == un.Alias.Value) { Compiler.Report.SymbolRelatedToPreviousError (a.Location, ""); Compiler.Report.Error (1537, un.Location, "The using alias `{0}' appeared previously in this namespace", un.Alias.Value); } } } clauses.Add (un); } public override void AddPartial (TypeDefinition next_part) { var existing = ns.LookupType (this, next_part.MemberName.Name, next_part.MemberName.Arity, LookupMode.Probing, Location.Null); var td = existing != null ? existing.MemberDefinition as TypeDefinition : null; AddPartial (next_part, td); } public override void AddTypeContainer (TypeContainer tc) { var mn = tc.MemberName; var name = mn.Basename; while (mn.Left != null) { mn = mn.Left; name = mn.Name; } var names_container = Parent == null ? Module : (TypeContainer) this; MemberCore mc; if (names_container.DefinedNames.TryGetValue (name, out mc)) { if (tc is NamespaceContainer && mc is NamespaceContainer) { AddTypeContainerMember (tc); return; } Report.SymbolRelatedToPreviousError (mc); if ((mc.ModFlags & Modifiers.PARTIAL) != 0 && (tc is ClassOrStruct || tc is Interface)) { Error_MissingPartialModifier (tc); } else { Report.Error (101, tc.Location, "The namespace `{0}' already contains a definition for `{1}'", GetSignatureForError (), mn.GetSignatureForError ()); } } else { names_container.DefinedNames.Add (name, tc); var tdef = tc.PartialContainer; if (tdef != null) { // // Same name conflict in different namespace containers // var conflict = ns.GetAllTypes (mn.Name); if (conflict != null) { foreach (var e in conflict) { if (e.Arity == mn.Arity) { mc = (MemberCore) e.MemberDefinition; break; } } } if (mc != null) { Report.SymbolRelatedToPreviousError (mc); Report.Error (101, tc.Location, "The namespace `{0}' already contains a definition for `{1}'", GetSignatureForError (), mn.GetSignatureForError ()); } else { ns.AddType (Module, tdef.Definition); } } } base.AddTypeContainer (tc); } public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) { throw new NotSupportedException (); } public override void EmitContainer () { VerifyClsCompliance (); base.EmitContainer (); } public ExtensionMethodCandidates LookupExtensionMethod (IMemberContext invocationContext, string name, int arity, int position) { // // Here we try to resume the search for extension method at the point // where the last bunch of candidates was found. It's more tricky than // it seems as we have to check both namespace containers and namespace // in correct order. // // Consider: // // namespace A { // using N1; // namespace B.C.D { // 0) { mn = mn.Left; container_ns = container_ns.Parent; } while (mn != null) { ++position; var methods = container_ns.LookupExtensionMethod (invocationContext, name, arity); if (methods != null) { return new ExtensionMethodCandidates (invocationContext, methods, container, position); } mn = mn.Left; container_ns = container_ns.Parent; } position = 0; container = container.Parent; } while (container != null); return null; } ExtensionMethodCandidates LookupExtensionMethodCandidates (IMemberContext invocationContext, string name, int arity, ref int position) { List candidates = null; if (position == 0) { ++position; candidates = ns.LookupExtensionMethod (invocationContext, name, arity); if (candidates != null) { return new ExtensionMethodCandidates (invocationContext, candidates, this, position); } } if (position == 1) { ++position; foreach (Namespace n in namespace_using_table) { var a = n.LookupExtensionMethod (invocationContext, name, arity); if (a == null) continue; if (candidates == null) candidates = a; else candidates.AddRange (a); } if (types_using_table != null) { foreach (var t in types_using_table) { var res = t.MemberCache.FindExtensionMethods (invocationContext, name, arity); if (res == null) continue; if (candidates == null) candidates = res; else candidates.AddRange (res); } } if (candidates != null) return new ExtensionMethodCandidates (invocationContext, candidates, this, position); } return null; } public override FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc) { // // Only simple names (no dots) will be looked up with this function // FullNamedExpression resolved; for (NamespaceContainer container = this; container != null; container = container.Parent) { resolved = container.Lookup (name, arity, mode, loc); if (resolved != null || container.MemberName == null) return resolved; var container_ns = container.ns.Parent; var mn = container.MemberName.Left; while (mn != null) { resolved = container_ns.LookupTypeOrNamespace (this, name, arity, mode, loc); if (resolved != null) return resolved; mn = mn.Left; container_ns = container_ns.Parent; } } return null; } public override void GetCompletionStartingWith (string prefix, List results) { if (Usings == null) return; foreach (var un in Usings) { if (un.Alias != null) continue; var name = un.NamespaceExpression.Name; if (name.StartsWith (prefix)) results.Add (name); } IEnumerable all = Enumerable.Empty (); foreach (Namespace using_ns in namespace_using_table) { if (prefix.StartsWith (using_ns.Name)) { int ld = prefix.LastIndexOf ('.'); if (ld != -1) { string rest = prefix.Substring (ld + 1); all = all.Concat (using_ns.CompletionGetTypesStartingWith (rest)); } } all = all.Concat (using_ns.CompletionGetTypesStartingWith (prefix)); } results.AddRange (all); base.GetCompletionStartingWith (prefix, results); } // // Looks-up a alias named @name in this and surrounding namespace declarations // public FullNamedExpression LookupExternAlias (string name) { if (aliases == null) return null; UsingAliasNamespace uan; if (aliases.TryGetValue (name, out uan) && uan is UsingExternAlias) return uan.ResolvedExpression; return null; } // // Looks-up a alias named @name in this and surrounding namespace declarations // public override FullNamedExpression LookupNamespaceAlias (string name) { for (NamespaceContainer n = this; n != null; n = n.Parent) { if (n.aliases == null) continue; UsingAliasNamespace uan; if (n.aliases.TryGetValue (name, out uan)) { if (uan.ResolvedExpression == null) uan.Define (n); return uan.ResolvedExpression; } } return null; } FullNamedExpression Lookup (string name, int arity, LookupMode mode, Location loc) { // // Check whether it's in the namespace. // FullNamedExpression fne = ns.LookupTypeOrNamespace (this, name, arity, mode, loc); // // Check aliases. // if (aliases != null && arity == 0) { UsingAliasNamespace uan; if (aliases.TryGetValue (name, out uan)) { if (fne != null && mode != LookupMode.Probing) { // TODO: Namespace has broken location //Report.SymbolRelatedToPreviousError (fne.Location, null); Compiler.Report.SymbolRelatedToPreviousError (uan.Location, null); Compiler.Report.Error (576, loc, "Namespace `{0}' contains a definition with same name as alias `{1}'", GetSignatureForError (), name); } if (uan.ResolvedExpression == null) uan.Define (this); return uan.ResolvedExpression; } } if (fne != null) return fne; // // Lookup can be called before the namespace is defined from different namespace using alias clause // if (namespace_using_table == null) { DoDefineNamespace (); } // // Check using entries. // FullNamedExpression match = null; foreach (Namespace using_ns in namespace_using_table) { // // A using directive imports only types contained in the namespace, it // does not import any nested namespaces // var t = using_ns.LookupType (this, name, arity, mode, loc); if (t == null) continue; fne = new TypeExpression (t, loc); if (match == null) { match = fne; continue; } // Prefer types over namespaces var texpr_fne = fne as TypeExpr; var texpr_match = match as TypeExpr; if (texpr_fne != null && texpr_match == null) { match = fne; continue; } else if (texpr_fne == null) { continue; } // It can be top level accessibility only var better = Namespace.IsImportedTypeOverride (Module, texpr_match.Type, texpr_fne.Type); if (better == null) { if (mode == LookupMode.Normal) { Error_AmbiguousReference (name, texpr_match, texpr_fne, loc); } return match; } if (better == texpr_fne.Type) match = texpr_fne; } if (types_using_table != null && (mode & LookupMode.IgnoreStaticUsing) == 0) { foreach (var using_type in types_using_table) { var type = MemberCache.FindNestedType (using_type, name, arity, true); if (type == null) continue; fne = new TypeExpression (type, loc); if (match == null) { match = fne; continue; } if (mode == LookupMode.Normal) { Error_AmbiguousReference (name, match, fne, loc); } } } return match; } void Error_AmbiguousReference (string name, FullNamedExpression a, FullNamedExpression b, Location loc) { var report = Compiler.Report; report.SymbolRelatedToPreviousError (a.Type); report.SymbolRelatedToPreviousError (b.Type); report.Error (104, loc, "`{0}' is an ambiguous reference between `{1}' and `{2}'", name, a.GetSignatureForError (), b.GetSignatureForError ()); } public static Expression LookupStaticUsings (IMemberContext mc, string name, int arity, Location loc) { for (var m = mc.CurrentMemberDefinition; m != null; m = m.Parent) { var nc = m as NamespaceContainer; if (nc == null) continue; List candidates = null; if (nc.types_using_table != null) { foreach (var using_type in nc.types_using_table) { var members = MemberCache.FindMembers (using_type, name, true); if (members == null) continue; foreach (var member in members) { if ((member.Kind & MemberKind.NestedMask) != 0) { // non-static nested type is included with using static } else { if ((member.Modifiers & Modifiers.STATIC) == 0) continue; if ((member.Modifiers & Modifiers.METHOD_EXTENSION) != 0) continue; } if (arity > 0 && member.Arity != arity) continue; if (candidates == null) candidates = new List (); candidates.Add (member); } } } if (candidates != null) { var expr = Expression.MemberLookupToExpression (mc, candidates, false, null, name, arity, Expression.MemberLookupRestrictions.None, loc); if (expr != null) return expr; } } return null; } protected override void DefineNamespace () { if (namespace_using_table == null) DoDefineNamespace (); base.DefineNamespace (); } void DoDefineNamespace () { namespace_using_table = empty_namespaces; if (clauses != null) { List namespaces = null; List types = null; bool post_process_using_aliases = false; for (int i = 0; i < clauses.Count; ++i) { var entry = clauses[i]; if (entry.Alias != null) { if (aliases == null) aliases = new Dictionary (); // // Aliases are not available when resolving using section // except extern aliases // if (entry is UsingExternAlias) { entry.Define (this); if (entry.ResolvedExpression != null) aliases.Add (entry.Alias.Value, (UsingExternAlias) entry); clauses.RemoveAt (i--); } else { post_process_using_aliases = true; } continue; } try { entry.Define (this); } finally { // // It's needed for repl only, when using clause cannot be resolved don't hold it in // global list which is resolved for every evaluation // if (entry.ResolvedExpression == null) { clauses.RemoveAt (i--); } } if (entry.ResolvedExpression == null) continue; var using_ns = entry.ResolvedExpression as NamespaceExpression; if (using_ns == null) { var type = entry.ResolvedExpression.Type; if (types == null) types = new List (); if (types.Contains (type)) { Warning_DuplicateEntry (entry); } else { types.Add (type); } } else { if (namespaces == null) namespaces = new List (); if (namespaces.Contains (using_ns.Namespace)) { // Ensure we don't report the warning multiple times in repl clauses.RemoveAt (i--); Warning_DuplicateEntry (entry); } else { namespaces.Add (using_ns.Namespace); } } } namespace_using_table = namespaces == null ? new Namespace [0] : namespaces.ToArray (); if (types != null) types_using_table = types.ToArray (); if (post_process_using_aliases) { for (int i = 0; i < clauses.Count; ++i) { var entry = clauses[i]; if (entry.Alias != null) { aliases[entry.Alias.Value] = (UsingAliasNamespace) entry; } } } } } protected override void DoDefineContainer () { base.DoDefineContainer (); if (clauses != null) { for (int i = 0; i < clauses.Count; ++i) { var entry = clauses[i]; // // Finish definition of using aliases not visited during container // definition // if (entry.Alias != null && entry.ResolvedExpression == null) { entry.Define (this); } } } } public void EnableRedefinition () { is_defined = false; namespace_using_table = null; } internal override void GenerateDocComment (DocumentationBuilder builder) { if (containers != null) { foreach (var tc in containers) tc.GenerateDocComment (builder); } } public override string GetSignatureForError () { return MemberName == null ? "global::" : base.GetSignatureForError (); } public override void RemoveContainer (TypeContainer cont) { base.RemoveContainer (cont); NS.RemoveContainer (cont); } protected override bool VerifyClsCompliance () { if (Module.IsClsComplianceRequired ()) { if (MemberName != null && MemberName.Name[0] == '_') { Warning_IdentifierNotCompliant (); } ns.VerifyClsCompliance (); return true; } return false; } void Warning_DuplicateEntry (UsingClause entry) { Compiler.Report.Warning (105, 3, entry.Location, "The using directive for `{0}' appeared previously in this namespace", entry.ResolvedExpression.GetSignatureForError ()); } public override void Accept (StructuralVisitor visitor) { visitor.Visit (this); } } public class UsingNamespace : UsingClause { public UsingNamespace (ATypeNameExpression expr, Location loc) : base (expr, loc) { } public override void Define (NamespaceContainer ctx) { base.Define (ctx); var ns = resolved as NamespaceExpression; if (ns != null) return; if (resolved != null) { var compiler = ctx.Module.Compiler; var type = resolved.Type; resolved = null; compiler.Report.SymbolRelatedToPreviousError (type); compiler.Report.Error (138, Location, "A `using' directive can only be applied to namespaces but `{0}' denotes a type. Consider using a `using static' instead", type.GetSignatureForError ()); } } } public class UsingType : UsingClause { public UsingType (ATypeNameExpression expr, Location loc) : base (expr, loc) { } public override void Define (NamespaceContainer ctx) { base.Define (ctx); if (resolved == null) return; var ns = resolved as NamespaceExpression; if (ns != null) { var compiler = ctx.Module.Compiler; compiler.Report.Error (7007, Location, "A 'using static' directive can only be applied to types but `{0}' denotes a namespace. Consider using a `using' directive instead", ns.GetSignatureForError ()); return; } // TODO: Need to move it to post_process_using_aliases //ObsoleteAttribute obsolete_attr = resolved.Type.GetAttributeObsolete (); //if (obsolete_attr != null) { // AttributeTester.Report_ObsoleteMessage (obsolete_attr, resolved.GetSignatureForError (), Location, ctx.Compiler.Report); //} } } public class UsingClause { readonly ATypeNameExpression expr; readonly Location loc; protected FullNamedExpression resolved; public UsingClause (ATypeNameExpression expr, Location loc) { this.expr = expr; this.loc = loc; } #region Properties public virtual SimpleMemberName Alias { get { return null; } } public Location Location { get { return loc; } } public ATypeNameExpression NamespaceExpression { get { return expr; } } public FullNamedExpression ResolvedExpression { get { return resolved; } } #endregion public string GetSignatureForError () { return expr.GetSignatureForError (); } public virtual void Define (NamespaceContainer ctx) { resolved = expr.ResolveAsTypeOrNamespace (ctx, false); } public override string ToString() { return resolved.ToString(); } } public class UsingExternAlias : UsingAliasNamespace { public UsingExternAlias (SimpleMemberName alias, Location loc) : base (alias, null, loc) { } public override void Define (NamespaceContainer ctx) { var ns = ctx.Module.GetRootNamespace (Alias.Value); if (ns == null) { ctx.Module.Compiler.Report.Error (430, Location, "The extern alias `{0}' was not specified in -reference option", Alias.Value); return; } resolved = new NamespaceExpression (ns, Location); } } public class UsingAliasNamespace : UsingNamespace { readonly SimpleMemberName alias; public struct AliasContext : IMemberContext { readonly NamespaceContainer ns; public AliasContext (NamespaceContainer ns) { this.ns = ns; } public TypeSpec CurrentType { get { return null; } } public TypeParameters CurrentTypeParameters { get { return null; } } public MemberCore CurrentMemberDefinition { get { return null; } } public bool IsObsolete { get { return false; } } public bool IsUnsafe { get { throw new NotImplementedException (); } } public bool IsStatic { get { throw new NotImplementedException (); } } public ModuleContainer Module { get { return ns.Module; } } public string GetSignatureForError () { throw new NotImplementedException (); } public ExtensionMethodCandidates LookupExtensionMethod (string name, int arity) { return null; } public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc) { var fne = ns.NS.LookupTypeOrNamespace (ns, name, arity, mode, loc); if (fne != null) return fne; // // Only extern aliases are allowed in this context // fne = ns.LookupExternAlias (name); if (fne != null || ns.MemberName == null) return fne; var container_ns = ns.NS.Parent; var mn = ns.MemberName.Left; while (mn != null) { fne = container_ns.LookupTypeOrNamespace (this, name, arity, mode, loc); if (fne != null) return fne; mn = mn.Left; container_ns = container_ns.Parent; } if (ns.Parent != null) return ns.Parent.LookupNamespaceOrType (name, arity, mode, loc); return null; } public FullNamedExpression LookupNamespaceAlias (string name) { return ns.LookupNamespaceAlias (name); } } public UsingAliasNamespace (SimpleMemberName alias, ATypeNameExpression expr, Location loc) : base (expr, loc) { this.alias = alias; } public override SimpleMemberName Alias { get { return alias; } } public override void Define (NamespaceContainer ctx) { // // The namespace-or-type-name of a using-alias-directive is resolved as if // the immediately containing compilation unit or namespace body had no // using-directives. A using-alias-directive may however be affected // by extern-alias-directives in the immediately containing compilation // unit or namespace body // // We achieve that by introducing alias-context which redirect any local // namespace or type resolve calls to parent namespace // resolved = NamespaceExpression.ResolveAsTypeOrNamespace (new AliasContext (ctx), false) ?? new TypeExpression (InternalType.ErrorType, NamespaceExpression.Location); } } }