mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1236 lines
32 KiB
1236 lines
32 KiB
// |
|
// 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. |
|
// |
|
using System; |
|
using System.Collections.Generic; |
|
using System.Linq; |
|
|
|
namespace Mono.CSharp { |
|
|
|
public class RootNamespace : Namespace { |
|
|
|
readonly string alias_name; |
|
readonly Dictionary<string, Namespace> all_namespaces; |
|
|
|
public RootNamespace (string alias_name) |
|
: base (null, String.Empty) |
|
{ |
|
this.alias_name = alias_name; |
|
|
|
all_namespaces = new Dictionary<string, Namespace> (); |
|
all_namespaces.Add ("", this); |
|
} |
|
|
|
public string Alias { |
|
get { |
|
return alias_name; |
|
} |
|
} |
|
|
|
public void RegisterNamespace (Namespace child) |
|
{ |
|
if (child != this) |
|
all_namespaces.Add (child.Name, child); |
|
} |
|
|
|
public bool IsNamespace (string name) |
|
{ |
|
return all_namespaces.ContainsKey (name); |
|
} |
|
|
|
protected void RegisterNamespace (string dotted_name) |
|
{ |
|
if (dotted_name != null && dotted_name.Length != 0 && ! IsNamespace (dotted_name)) |
|
GetNamespace (dotted_name, true); |
|
} |
|
|
|
public override string GetSignatureForError () |
|
{ |
|
return alias_name + "::"; |
|
} |
|
} |
|
|
|
public class GlobalRootNamespace : RootNamespace |
|
{ |
|
public GlobalRootNamespace () |
|
: base ("global") |
|
{ |
|
} |
|
|
|
public override void Error_NamespaceDoesNotExist (Location loc, string name, int arity, IMemberContext ctx) |
|
{ |
|
ctx.Compiler.Report.Error (400, loc, |
|
"The type or namespace name `{0}' could not be found in the global namespace (are you missing an assembly reference?)", |
|
name); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Keeps track of the namespaces defined in the C# code. |
|
/// |
|
/// This is an Expression to allow it to be referenced in the |
|
/// compiler parse/intermediate tree during name resolution. |
|
/// </summary> |
|
public class Namespace : FullNamedExpression { |
|
|
|
Namespace parent; |
|
string fullname; |
|
protected Dictionary<string, Namespace> namespaces; |
|
protected Dictionary<string, IList<TypeSpec>> types; |
|
Dictionary<string, TypeExpr> cached_types; |
|
RootNamespace root; |
|
bool cls_checked; |
|
|
|
public readonly MemberName MemberName; |
|
|
|
/// <summary> |
|
/// Constructor Takes the current namespace and the |
|
/// name. This is bootstrapped with parent == null |
|
/// and name = "" |
|
/// </summary> |
|
public Namespace (Namespace parent, string name) |
|
{ |
|
// Expression members. |
|
this.eclass = ExprClass.Namespace; |
|
this.Type = InternalType.FakeInternalType; |
|
this.loc = Location.Null; |
|
|
|
this.parent = parent; |
|
|
|
if (parent != null) |
|
this.root = parent.root; |
|
else |
|
this.root = this as RootNamespace; |
|
|
|
if (this.root == null) |
|
throw new InternalErrorException ("Root namespaces must be created using RootNamespace"); |
|
|
|
string pname = parent != null ? parent.fullname : ""; |
|
|
|
if (pname == "") |
|
fullname = name; |
|
else |
|
fullname = parent.fullname + "." + name; |
|
|
|
if (fullname == null) |
|
throw new InternalErrorException ("Namespace has a null fullname"); |
|
|
|
if (parent != null && parent.MemberName != MemberName.Null) |
|
MemberName = new MemberName (parent.MemberName, name); |
|
else if (name.Length == 0) |
|
MemberName = MemberName.Null; |
|
else |
|
MemberName = new MemberName (name); |
|
|
|
namespaces = new Dictionary<string, Namespace> (); |
|
cached_types = new Dictionary<string, TypeExpr> (); |
|
|
|
root.RegisterNamespace (this); |
|
} |
|
|
|
#region Properties |
|
|
|
/// <summary> |
|
/// The qualified name of the current namespace |
|
/// </summary> |
|
public string Name { |
|
get { return fullname; } |
|
} |
|
|
|
/// <summary> |
|
/// The parent of this namespace, used by the parser to "Pop" |
|
/// the current namespace declaration |
|
/// </summary> |
|
public Namespace Parent { |
|
get { return parent; } |
|
} |
|
|
|
#endregion |
|
|
|
protected override Expression DoResolve (ResolveContext ec) |
|
{ |
|
return this; |
|
} |
|
|
|
public virtual void Error_NamespaceDoesNotExist (Location loc, string name, int arity, IMemberContext ctx) |
|
{ |
|
FullNamedExpression retval = Lookup (ctx.Compiler, name, -System.Math.Max (1, arity), loc); |
|
if (retval != null) { |
|
Error_TypeArgumentsCannotBeUsed (ctx.Compiler.Report, loc, retval.Type, arity); |
|
return; |
|
} |
|
|
|
Namespace ns; |
|
if (arity > 0 && namespaces.TryGetValue (name, out ns)) { |
|
ns.Error_TypeArgumentsCannotBeUsed (ctx.Compiler.Report, loc, null, arity); |
|
return; |
|
} |
|
|
|
ctx.Compiler.Report.Error (234, loc, |
|
"The type or namespace name `{0}' does not exist in the namespace `{1}'. Are you missing an assembly reference?", |
|
name, GetSignatureForError ()); |
|
} |
|
|
|
public override string GetSignatureForError () |
|
{ |
|
return fullname; |
|
} |
|
|
|
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 TypeExpr LookupType (CompilerContext ctx, string name, int arity, bool silent, Location loc) |
|
{ |
|
if (types == null) |
|
return null; |
|
|
|
TypeExpr te; |
|
if (arity == 0 && cached_types.TryGetValue (name, out te)) |
|
return te; |
|
|
|
IList<TypeSpec> found; |
|
if (!types.TryGetValue (name, out found)) |
|
return null; |
|
|
|
TypeSpec best = null; |
|
foreach (var ts in found) { |
|
if (ts.Arity == arity) { |
|
if (best == null) { |
|
best = ts; |
|
continue; |
|
} |
|
|
|
var pts = best as BuildinTypeSpec; |
|
if (pts == null) |
|
pts = ts as BuildinTypeSpec; |
|
|
|
if (pts != null) { |
|
ctx.Report.SymbolRelatedToPreviousError (best); |
|
ctx.Report.SymbolRelatedToPreviousError (ts); |
|
|
|
// TODO: This should use different warning number but we want to be csc compatible |
|
ctx.Report.Warning (1685, 1, loc, |
|
"The predefined type `{0}.{1}' is redefined in the source code. Ignoring the local type definition", |
|
pts.Namespace, pts.Name); |
|
best = pts; |
|
continue; |
|
} |
|
|
|
if (best.MemberDefinition.IsImported && ts.MemberDefinition.IsImported) { |
|
ctx.Report.SymbolRelatedToPreviousError (best); |
|
ctx.Report.SymbolRelatedToPreviousError (ts); |
|
if (silent) { |
|
ctx.Report.Warning (1685, 1, loc, |
|
"The predefined type `{0}' is defined in multiple assemblies. Using definition from `{1}'", |
|
ts.GetSignatureForError (), best.MemberDefinition.DeclaringAssembly.Name); |
|
} else { |
|
ctx.Report.Error (433, loc, "The imported type `{0}' is defined multiple times", ts.GetSignatureForError ()); |
|
} |
|
|
|
break; |
|
} |
|
|
|
if (best.MemberDefinition.IsImported) |
|
best = ts; |
|
|
|
if ((best.Modifiers & Modifiers.INTERNAL) != 0 && !best.MemberDefinition.IsInternalAsPublic (RootContext.ToplevelTypes.DeclaringAssembly)) |
|
continue; |
|
|
|
if (silent) |
|
continue; |
|
|
|
if (ts.MemberDefinition.IsImported) |
|
ctx.Report.SymbolRelatedToPreviousError (ts); |
|
|
|
ctx.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 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; |
|
} |
|
} |
|
} |
|
|
|
if (best == null) |
|
return null; |
|
|
|
if ((best.Modifiers & Modifiers.INTERNAL) != 0 && !best.MemberDefinition.IsInternalAsPublic (RootContext.ToplevelTypes.DeclaringAssembly)) |
|
return null; |
|
|
|
te = new TypeExpression (best, Location.Null); |
|
|
|
// TODO MemberCache: Cache more |
|
if (arity == 0 && !silent) |
|
cached_types.Add (name, te); |
|
|
|
return te; |
|
} |
|
|
|
TypeSpec LookupType (string name, int arity) |
|
{ |
|
if (types == null) |
|
return null; |
|
|
|
IList<TypeSpec> found; |
|
if (types.TryGetValue (name, out found)) { |
|
TypeSpec best = null; |
|
|
|
foreach (var ts in found) { |
|
if (ts.Arity == arity) |
|
return ts; |
|
|
|
// |
|
// Lookup for the best candidate with 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; |
|
} |
|
} |
|
} |
|
|
|
return best; |
|
} |
|
|
|
return null; |
|
} |
|
|
|
public FullNamedExpression Lookup (CompilerContext ctx, string name, int arity, Location loc) |
|
{ |
|
if (arity == 0 && namespaces.ContainsKey (name)) |
|
return namespaces [name]; |
|
|
|
return LookupType (ctx, name, arity, false, loc); |
|
} |
|
|
|
// |
|
// Completes types with the given `prefix' |
|
// |
|
public IEnumerable<string> CompletionGetTypesStartingWith (string prefix) |
|
{ |
|
if (types == null) |
|
return Enumerable.Empty<string> (); |
|
|
|
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<MethodSpec> LookupExtensionMethod (TypeSpec extensionType, TypeContainer invocationContext, string name, int arity) |
|
{ |
|
if (types == null) |
|
return null; |
|
|
|
List<MethodSpec> found = null; |
|
|
|
// TODO: Add per namespace flag when at least 1 type has extension |
|
|
|
foreach (var tgroup in types.Values) { |
|
foreach (var ts in tgroup) { |
|
if ((ts.Modifiers & Modifiers.METHOD_EXTENSION) == 0) |
|
continue; |
|
|
|
var res = ts.MemberCache.FindExtensionMethods (invocationContext, extensionType, name, arity); |
|
if (res == null) |
|
continue; |
|
|
|
if (found == null) { |
|
found = res; |
|
} else { |
|
found.AddRange (res); |
|
} |
|
} |
|
} |
|
|
|
return found; |
|
} |
|
|
|
public void AddType (TypeSpec ts) |
|
{ |
|
if (types == null) { |
|
types = new Dictionary<string, IList<TypeSpec>> (64); |
|
} |
|
|
|
var name = ts.Name; |
|
IList<TypeSpec> 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 (ts, found); |
|
if (better_type == found) |
|
return; |
|
|
|
if (better_type != null) { |
|
existing [0] = better_type; |
|
return; |
|
} |
|
} |
|
|
|
existing = new List<TypeSpec> (); |
|
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 (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 (TypeSpec ts, TypeSpec found) |
|
{ |
|
var ts_accessible = (ts.Modifiers & Modifiers.PUBLIC) != 0 || ts.MemberDefinition.IsInternalAsPublic (RootContext.ToplevelTypes.DeclaringAssembly); |
|
var found_accessible = (found.Modifiers & Modifiers.PUBLIC) != 0 || found.MemberDefinition.IsInternalAsPublic (RootContext.ToplevelTypes.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 RemoveDeclSpace (string name) |
|
{ |
|
types.Remove (name); |
|
} |
|
|
|
public void ReplaceTypeWithPredefined (TypeSpec ts, BuildinTypeSpec pts) |
|
{ |
|
var found = types [ts.Name]; |
|
cached_types.Remove (ts.Name); |
|
if (found.Count == 1) { |
|
types[ts.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<string, List<TypeSpec>> (StringComparer.OrdinalIgnoreCase); |
|
foreach (var tgroup in types.Values) { |
|
foreach (var tm in tgroup) { |
|
if ((tm.Modifiers & Modifiers.PUBLIC) == 0 || !tm.IsCLSCompliant ()) |
|
continue; |
|
|
|
List<TypeSpec> found; |
|
if (!locase_types.TryGetValue (tm.Name, out found)) { |
|
found = new List<TypeSpec> (); |
|
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 ()); |
|
} |
|
} |
|
} |
|
|
|
// |
|
// Namespace container as created by the parser |
|
// |
|
public class NamespaceEntry : IMemberContext { |
|
|
|
public class UsingEntry { |
|
readonly MemberName name; |
|
Namespace resolved; |
|
|
|
public UsingEntry (MemberName name) |
|
{ |
|
this.name = name; |
|
} |
|
|
|
public string GetSignatureForError () |
|
{ |
|
return name.GetSignatureForError (); |
|
} |
|
|
|
public Location Location { |
|
get { return name.Location; } |
|
} |
|
|
|
public MemberName MemberName { |
|
get { return name; } |
|
} |
|
|
|
public string Name { |
|
get { return GetSignatureForError (); } |
|
} |
|
|
|
public Namespace Resolve (IMemberContext rc) |
|
{ |
|
if (resolved != null) |
|
return resolved; |
|
|
|
FullNamedExpression fne = name.GetTypeExpression ().ResolveAsTypeStep (rc, false); |
|
if (fne == null) |
|
return null; |
|
|
|
resolved = fne as Namespace; |
|
if (resolved == null) { |
|
rc.Compiler.Report.SymbolRelatedToPreviousError (fne.Type); |
|
rc.Compiler.Report.Error (138, Location, |
|
"`{0}' is a type not a namespace. A using namespace directive can only be applied to namespaces", |
|
GetSignatureForError ()); |
|
} |
|
return resolved; |
|
} |
|
|
|
public override string ToString () |
|
{ |
|
return Name; |
|
} |
|
} |
|
|
|
public class UsingAliasEntry { |
|
public readonly string Alias; |
|
public Location Location; |
|
|
|
public UsingAliasEntry (string alias, Location loc) |
|
{ |
|
this.Alias = alias; |
|
this.Location = loc; |
|
} |
|
|
|
public virtual FullNamedExpression Resolve (IMemberContext rc, bool local) |
|
{ |
|
FullNamedExpression fne = rc.Module.GetRootNamespace (Alias); |
|
if (fne == null) { |
|
rc.Compiler.Report.Error (430, Location, |
|
"The extern alias `{0}' was not specified in -reference option", |
|
Alias); |
|
} |
|
|
|
return fne; |
|
} |
|
|
|
public override string ToString () |
|
{ |
|
return Alias; |
|
} |
|
|
|
} |
|
|
|
class LocalUsingAliasEntry : UsingAliasEntry { |
|
FullNamedExpression resolved; |
|
MemberName value; |
|
|
|
public LocalUsingAliasEntry (string alias, MemberName name, Location loc) |
|
: base (alias, loc) |
|
{ |
|
this.value = name; |
|
} |
|
|
|
public override FullNamedExpression Resolve (IMemberContext rc, bool local) |
|
{ |
|
if (resolved != null || value == null) |
|
return resolved; |
|
|
|
if (local) |
|
return null; |
|
|
|
resolved = value.GetTypeExpression ().ResolveAsTypeStep (rc, false); |
|
if (resolved == null) { |
|
value = null; |
|
return null; |
|
} |
|
|
|
if (resolved is TypeExpr) |
|
resolved = resolved.ResolveAsTypeTerminal (rc, false); |
|
|
|
return resolved; |
|
} |
|
|
|
public override string ToString () |
|
{ |
|
return String.Format ("{0} = {1}", Alias, value.GetSignatureForError ()); |
|
} |
|
} |
|
|
|
Namespace ns; |
|
NamespaceEntry parent, implicit_parent; |
|
CompilationUnit file; |
|
int symfile_id; |
|
|
|
// Namespace using import block |
|
List<UsingAliasEntry> using_aliases; |
|
List<UsingEntry> using_clauses; |
|
public bool DeclarationFound; |
|
// End |
|
|
|
public readonly bool IsImplicit; |
|
public readonly DeclSpace SlaveDeclSpace; |
|
static readonly Namespace [] empty_namespaces = new Namespace [0]; |
|
Namespace [] namespace_using_table; |
|
ModuleContainer ctx; |
|
|
|
static List<NamespaceEntry> entries = new List<NamespaceEntry> (); |
|
|
|
public static void Reset () |
|
{ |
|
entries = new List<NamespaceEntry> (); |
|
} |
|
|
|
public NamespaceEntry (ModuleContainer ctx, NamespaceEntry parent, CompilationUnit file, string name) |
|
{ |
|
this.ctx = ctx; |
|
this.parent = parent; |
|
this.file = file; |
|
entries.Add (this); |
|
|
|
if (parent != null) |
|
ns = parent.NS.GetNamespace (name, true); |
|
else if (name != null) |
|
ns = ctx.GlobalRootNamespace.GetNamespace (name, true); |
|
else |
|
ns = ctx.GlobalRootNamespace; |
|
|
|
SlaveDeclSpace = new RootDeclSpace (this); |
|
} |
|
|
|
private NamespaceEntry (ModuleContainer ctx, NamespaceEntry parent, CompilationUnit file, Namespace ns, bool slave) |
|
{ |
|
this.ctx = ctx; |
|
this.parent = parent; |
|
this.file = file; |
|
this.IsImplicit = true; |
|
this.ns = ns; |
|
this.SlaveDeclSpace = slave ? new RootDeclSpace (this) : null; |
|
} |
|
|
|
// |
|
// Populates the Namespace with some using declarations, used by the |
|
// eval mode. |
|
// |
|
public void Populate (List<UsingAliasEntry> source_using_aliases, List<UsingEntry> source_using_clauses) |
|
{ |
|
foreach (UsingAliasEntry uae in source_using_aliases){ |
|
if (using_aliases == null) |
|
using_aliases = new List<UsingAliasEntry> (); |
|
|
|
using_aliases.Add (uae); |
|
} |
|
|
|
foreach (UsingEntry ue in source_using_clauses){ |
|
if (using_clauses == null) |
|
using_clauses = new List<UsingEntry> (); |
|
|
|
using_clauses.Add (ue); |
|
} |
|
} |
|
|
|
// |
|
// Extracts the using alises and using clauses into a couple of |
|
// arrays that might already have the same information; Used by the |
|
// C# Eval mode. |
|
// |
|
public void Extract (List<UsingAliasEntry> out_using_aliases, List<UsingEntry> out_using_clauses) |
|
{ |
|
if (using_aliases != null){ |
|
foreach (UsingAliasEntry uae in using_aliases){ |
|
bool replaced = false; |
|
|
|
for (int i = 0; i < out_using_aliases.Count; i++){ |
|
UsingAliasEntry out_uea = (UsingAliasEntry) out_using_aliases [i]; |
|
|
|
if (out_uea.Alias == uae.Alias){ |
|
out_using_aliases [i] = uae; |
|
replaced = true; |
|
break; |
|
} |
|
} |
|
if (!replaced) |
|
out_using_aliases.Add (uae); |
|
} |
|
} |
|
|
|
if (using_clauses != null){ |
|
foreach (UsingEntry ue in using_clauses){ |
|
bool found = false; |
|
|
|
foreach (UsingEntry out_ue in out_using_clauses) |
|
if (out_ue.Name == ue.Name){ |
|
found = true; |
|
break; |
|
} |
|
if (!found) |
|
out_using_clauses.Add (ue); |
|
} |
|
} |
|
} |
|
|
|
// |
|
// According to section 16.3.1 (using-alias-directive), the namespace-or-type-name is |
|
// resolved as if the immediately containing namespace body has no using-directives. |
|
// |
|
// Section 16.3.2 says that the same rule is applied when resolving the namespace-name |
|
// in the using-namespace-directive. |
|
// |
|
// To implement these rules, the expressions in the using directives are resolved using |
|
// the "doppelganger" (ghostly bodiless duplicate). |
|
// |
|
NamespaceEntry doppelganger; |
|
NamespaceEntry Doppelganger { |
|
get { |
|
if (!IsImplicit && doppelganger == null) { |
|
doppelganger = new NamespaceEntry (ctx, ImplicitParent, file, ns, true); |
|
doppelganger.using_aliases = using_aliases; |
|
} |
|
return doppelganger; |
|
} |
|
} |
|
|
|
public Namespace NS { |
|
get { return ns; } |
|
} |
|
|
|
public NamespaceEntry Parent { |
|
get { return parent; } |
|
} |
|
|
|
public NamespaceEntry ImplicitParent { |
|
get { |
|
if (parent == null) |
|
return null; |
|
if (implicit_parent == null) { |
|
implicit_parent = (parent.NS == ns.Parent) |
|
? parent |
|
: new NamespaceEntry (ctx, parent, file, ns.Parent, false); |
|
} |
|
return implicit_parent; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Records a new namespace for resolving name references |
|
/// </summary> |
|
public void AddUsing (MemberName name, Location loc) |
|
{ |
|
if (DeclarationFound){ |
|
Compiler.Report.Error (1529, loc, "A using clause must precede all other namespace elements except extern alias declarations"); |
|
} |
|
|
|
if (using_clauses == null) { |
|
using_clauses = new List<UsingEntry> (); |
|
} else { |
|
foreach (UsingEntry old_entry in using_clauses) { |
|
if (name.Equals (old_entry.MemberName)) { |
|
Compiler.Report.SymbolRelatedToPreviousError (old_entry.Location, old_entry.GetSignatureForError ()); |
|
Compiler.Report.Warning (105, 3, loc, "The using directive for `{0}' appeared previously in this namespace", name.GetSignatureForError ()); |
|
return; |
|
} |
|
} |
|
} |
|
|
|
using_clauses.Add (new UsingEntry (name)); |
|
} |
|
|
|
public void AddUsingAlias (string alias, MemberName name, Location loc) |
|
{ |
|
// TODO: This is parser bussines |
|
if (DeclarationFound){ |
|
Compiler.Report.Error (1529, loc, "A using clause must precede all other namespace elements except extern alias declarations"); |
|
} |
|
|
|
if (RootContext.Version != LanguageVersion.ISO_1 && alias == "global") |
|
Compiler.Report.Warning (440, 2, loc, "An alias named `global' will not be used when resolving 'global::';" + |
|
" the global namespace will be used instead"); |
|
|
|
AddUsingAlias (new LocalUsingAliasEntry (alias, name, loc)); |
|
} |
|
|
|
public void AddUsingExternalAlias (string alias, Location loc, Report Report) |
|
{ |
|
// TODO: Do this in parser |
|
bool not_first = using_clauses != null || DeclarationFound; |
|
if (using_aliases != null && !not_first) { |
|
foreach (UsingAliasEntry uae in using_aliases) { |
|
if (uae is LocalUsingAliasEntry) { |
|
not_first = true; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
if (not_first) |
|
Report.Error (439, loc, "An extern alias declaration must precede all other elements"); |
|
|
|
if (alias == "global") { |
|
Error_GlobalNamespaceRedefined (loc, Report); |
|
return; |
|
} |
|
|
|
AddUsingAlias (new UsingAliasEntry (alias, loc)); |
|
} |
|
|
|
void AddUsingAlias (UsingAliasEntry uae) |
|
{ |
|
if (using_aliases == null) { |
|
using_aliases = new List<UsingAliasEntry> (); |
|
} else { |
|
foreach (UsingAliasEntry entry in using_aliases) { |
|
if (uae.Alias == entry.Alias) { |
|
Compiler.Report.SymbolRelatedToPreviousError (uae.Location, uae.Alias); |
|
Compiler.Report.Error (1537, entry.Location, "The using alias `{0}' appeared previously in this namespace", |
|
entry.Alias); |
|
return; |
|
} |
|
} |
|
} |
|
|
|
using_aliases.Add (uae); |
|
} |
|
|
|
/// |
|
/// Does extension methods look up to find a method which matches name and extensionType. |
|
/// Search starts from this namespace and continues hierarchically up to top level. |
|
/// |
|
public IList<MethodSpec> LookupExtensionMethod (TypeSpec extensionType, string name, int arity, ref NamespaceEntry scope) |
|
{ |
|
List<MethodSpec> candidates = null; |
|
foreach (Namespace n in GetUsingTable ()) { |
|
var a = n.LookupExtensionMethod (extensionType, RootContext.ToplevelTypes, name, arity); |
|
if (a == null) |
|
continue; |
|
|
|
if (candidates == null) |
|
candidates = a; |
|
else |
|
candidates.AddRange (a); |
|
} |
|
|
|
scope = parent; |
|
if (candidates != null) |
|
return candidates; |
|
|
|
if (parent == null) |
|
return null; |
|
|
|
// |
|
// Inspect parent namespaces in namespace expression |
|
// |
|
Namespace parent_ns = ns.Parent; |
|
do { |
|
candidates = parent_ns.LookupExtensionMethod (extensionType, RootContext.ToplevelTypes, name, arity); |
|
if (candidates != null) |
|
return candidates; |
|
|
|
parent_ns = parent_ns.Parent; |
|
} while (parent_ns != null); |
|
|
|
// |
|
// Continue in parent scope |
|
// |
|
return parent.LookupExtensionMethod (extensionType, name, arity, ref scope); |
|
} |
|
|
|
public FullNamedExpression LookupNamespaceOrType (string name, int arity, Location loc, bool ignore_cs0104) |
|
{ |
|
// Precondition: Only simple names (no dots) will be looked up with this function. |
|
FullNamedExpression resolved = null; |
|
for (NamespaceEntry curr_ns = this; curr_ns != null; curr_ns = curr_ns.ImplicitParent) { |
|
if ((resolved = curr_ns.Lookup (name, arity, loc, ignore_cs0104)) != null) |
|
break; |
|
} |
|
|
|
return resolved; |
|
} |
|
|
|
public IList<string> CompletionGetTypesStartingWith (string prefix) |
|
{ |
|
IEnumerable<string> all = Enumerable.Empty<string> (); |
|
|
|
for (NamespaceEntry curr_ns = this; curr_ns != null; curr_ns = curr_ns.ImplicitParent){ |
|
foreach (Namespace using_ns in GetUsingTable ()){ |
|
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)); |
|
} |
|
} |
|
|
|
return all.Distinct ().ToList (); |
|
} |
|
|
|
// Looks-up a alias named @name in this and surrounding namespace declarations |
|
public FullNamedExpression LookupNamespaceAlias (string name) |
|
{ |
|
for (NamespaceEntry n = this; n != null; n = n.ImplicitParent) { |
|
if (n.using_aliases == null) |
|
continue; |
|
|
|
foreach (UsingAliasEntry ue in n.using_aliases) { |
|
if (ue.Alias == name) |
|
return ue.Resolve (Doppelganger ?? this, Doppelganger == null); |
|
} |
|
} |
|
|
|
return null; |
|
} |
|
|
|
private FullNamedExpression Lookup (string name, int arity, Location loc, bool ignore_cs0104) |
|
{ |
|
// |
|
// Check whether it's in the namespace. |
|
// |
|
FullNamedExpression fne = ns.Lookup (Compiler, name, arity, loc); |
|
|
|
// |
|
// Check aliases. |
|
// |
|
if (using_aliases != null && arity == 0) { |
|
foreach (UsingAliasEntry ue in using_aliases) { |
|
if (ue.Alias == name) { |
|
if (fne != null) { |
|
if (Doppelganger != null) { |
|
// TODO: Namespace has broken location |
|
//Report.SymbolRelatedToPreviousError (fne.Location, null); |
|
Compiler.Report.SymbolRelatedToPreviousError (ue.Location, null); |
|
Compiler.Report.Error (576, loc, |
|
"Namespace `{0}' contains a definition with same name as alias `{1}'", |
|
GetSignatureForError (), name); |
|
} else { |
|
return fne; |
|
} |
|
} |
|
|
|
return ue.Resolve (Doppelganger ?? this, Doppelganger == null); |
|
} |
|
} |
|
} |
|
|
|
if (fne != null) { |
|
if (!((fne.Type.Modifiers & Modifiers.INTERNAL) != 0 && !fne.Type.MemberDefinition.IsInternalAsPublic (RootContext.ToplevelTypes.DeclaringAssembly))) |
|
return fne; |
|
} |
|
|
|
if (IsImplicit) |
|
return null; |
|
|
|
// |
|
// Check using entries. |
|
// |
|
FullNamedExpression match = null; |
|
foreach (Namespace using_ns in GetUsingTable ()) { |
|
// A using directive imports only types contained in the namespace, it |
|
// does not import any nested namespaces |
|
fne = using_ns.LookupType (Compiler, name, arity, false, loc); |
|
if (fne == null) |
|
continue; |
|
|
|
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; |
|
} |
|
|
|
if (ignore_cs0104) |
|
return match; |
|
|
|
// It can be top level accessibility only |
|
var better = Namespace.IsImportedTypeOverride (texpr_match.Type, texpr_fne.Type); |
|
if (better == null) { |
|
Compiler.Report.SymbolRelatedToPreviousError (texpr_match.Type); |
|
Compiler.Report.SymbolRelatedToPreviousError (texpr_fne.Type); |
|
Compiler.Report.Error (104, loc, "`{0}' is an ambiguous reference between `{1}' and `{2}'", |
|
name, texpr_match.GetSignatureForError (), texpr_fne.GetSignatureForError ()); |
|
return match; |
|
} |
|
|
|
if (better == texpr_fne.Type) |
|
match = texpr_fne; |
|
} |
|
|
|
return match; |
|
} |
|
|
|
Namespace [] GetUsingTable () |
|
{ |
|
if (namespace_using_table != null) |
|
return namespace_using_table; |
|
|
|
if (using_clauses == null) { |
|
namespace_using_table = empty_namespaces; |
|
return namespace_using_table; |
|
} |
|
|
|
var list = new List<Namespace> (using_clauses.Count); |
|
|
|
foreach (UsingEntry ue in using_clauses) { |
|
Namespace using_ns = ue.Resolve (Doppelganger); |
|
if (using_ns == null) |
|
continue; |
|
|
|
list.Add (using_ns); |
|
} |
|
|
|
namespace_using_table = list.ToArray (); |
|
return namespace_using_table; |
|
} |
|
|
|
static readonly string [] empty_using_list = new string [0]; |
|
|
|
public int SymbolFileID { |
|
get { |
|
if (symfile_id == 0 && file.SourceFileEntry != null) { |
|
int parent_id = parent == null ? 0 : parent.SymbolFileID; |
|
|
|
string [] using_list = empty_using_list; |
|
if (using_clauses != null) { |
|
using_list = new string [using_clauses.Count]; |
|
for (int i = 0; i < using_clauses.Count; i++) |
|
using_list [i] = ((UsingEntry) using_clauses [i]).MemberName.GetName (); |
|
} |
|
|
|
symfile_id = SymbolWriter.DefineNamespace (ns.Name, file.CompileUnitEntry, using_list, parent_id); |
|
} |
|
return symfile_id; |
|
} |
|
} |
|
|
|
static void MsgtryRef (string s) |
|
{ |
|
Console.WriteLine (" Try using -r:" + s); |
|
} |
|
|
|
static void MsgtryPkg (string s) |
|
{ |
|
Console.WriteLine (" Try using -pkg:" + s); |
|
} |
|
|
|
public static void Error_GlobalNamespaceRedefined (Location loc, Report Report) |
|
{ |
|
Report.Error (1681, loc, "The global extern alias cannot be redefined"); |
|
} |
|
|
|
public static void Error_NamespaceNotFound (Location loc, string name, Report Report) |
|
{ |
|
Report.Error (246, loc, "The type or namespace name `{0}' could not be found. Are you missing a using directive or an assembly reference?", |
|
name); |
|
|
|
switch (name) { |
|
case "Gtk": case "GtkSharp": |
|
MsgtryPkg ("gtk-sharp"); |
|
break; |
|
|
|
case "Gdk": case "GdkSharp": |
|
MsgtryPkg ("gdk-sharp"); |
|
break; |
|
|
|
case "Glade": case "GladeSharp": |
|
MsgtryPkg ("glade-sharp"); |
|
break; |
|
|
|
case "System.Drawing": |
|
case "System.Web.Services": |
|
case "System.Web": |
|
case "System.Data": |
|
case "System.Windows.Forms": |
|
MsgtryRef (name); |
|
break; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Used to validate that all the using clauses are correct |
|
/// after we are finished parsing all the files. |
|
/// </summary> |
|
void VerifyUsing () |
|
{ |
|
if (using_aliases != null) { |
|
foreach (UsingAliasEntry ue in using_aliases) |
|
ue.Resolve (Doppelganger, Doppelganger == null); |
|
} |
|
|
|
if (using_clauses != null) { |
|
foreach (UsingEntry ue in using_clauses) |
|
ue.Resolve (Doppelganger); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Used to validate that all the using clauses are correct |
|
/// after we are finished parsing all the files. |
|
/// </summary> |
|
static public void VerifyAllUsing () |
|
{ |
|
foreach (NamespaceEntry entry in entries) |
|
entry.VerifyUsing (); |
|
} |
|
|
|
public string GetSignatureForError () |
|
{ |
|
return ns.GetSignatureForError (); |
|
} |
|
|
|
public override string ToString () |
|
{ |
|
return ns.ToString (); |
|
} |
|
|
|
#region IMemberContext Members |
|
|
|
public CompilerContext Compiler { |
|
get { return ctx.Compiler; } |
|
} |
|
|
|
public TypeSpec CurrentType { |
|
get { return SlaveDeclSpace.CurrentType; } |
|
} |
|
|
|
public MemberCore CurrentMemberDefinition { |
|
get { return SlaveDeclSpace.CurrentMemberDefinition; } |
|
} |
|
|
|
public TypeParameter[] CurrentTypeParameters { |
|
get { return SlaveDeclSpace.CurrentTypeParameters; } |
|
} |
|
|
|
// FIXME: It's false for expression types |
|
public bool HasUnresolvedConstraints { |
|
get { return true; } |
|
} |
|
|
|
public bool IsObsolete { |
|
get { return SlaveDeclSpace.IsObsolete; } |
|
} |
|
|
|
public bool IsUnsafe { |
|
get { return SlaveDeclSpace.IsUnsafe; } |
|
} |
|
|
|
public bool IsStatic { |
|
get { return SlaveDeclSpace.IsStatic; } |
|
} |
|
|
|
public ModuleContainer Module { |
|
get { return SlaveDeclSpace.Module; } |
|
} |
|
|
|
#endregion |
|
} |
|
}
|
|
|