[mcs] nameof operator
authorMarek Safar <marek.safar@gmail.com>
Tue, 12 Aug 2014 06:15:55 +0000 (08:15 +0200)
committerMarek Safar <marek.safar@gmail.com>
Tue, 12 Aug 2014 06:16:21 +0000 (08:16 +0200)
15 files changed:
mcs/errors/cs0426-4.cs [new file with mode: 0644]
mcs/errors/cs1001-9.cs [new file with mode: 0644]
mcs/errors/cs1644-43.cs [new file with mode: 0644]
mcs/errors/cs8071-2.cs [new file with mode: 0644]
mcs/errors/cs8071.cs [new file with mode: 0644]
mcs/mcs/constant.cs
mcs/mcs/context.cs
mcs/mcs/ecore.cs
mcs/mcs/expression.cs
mcs/mcs/namespace.cs
mcs/tests/test-nameof-01.cs [new file with mode: 0644]
mcs/tests/test-nameof-02.cs [new file with mode: 0644]
mcs/tests/test-nameof-03.cs [new file with mode: 0644]
mcs/tests/test-nameof-04.cs [new file with mode: 0644]
mcs/tests/ver-il-net_4_5.xml

diff --git a/mcs/errors/cs0426-4.cs b/mcs/errors/cs0426-4.cs
new file mode 100644 (file)
index 0000000..85024f7
--- /dev/null
@@ -0,0 +1,12 @@
+// CS0426: The nested type `WriteLINE' does not exist in the type `System.Console'
+// Line: 10
+
+using System;
+
+public class Test
+{
+       public static void Main ()
+       {
+               var x = nameof (Console.WriteLINE);
+       }
+}
diff --git a/mcs/errors/cs1001-9.cs b/mcs/errors/cs1001-9.cs
new file mode 100644 (file)
index 0000000..989f314
--- /dev/null
@@ -0,0 +1,10 @@
+// CS1001: Identifier expected
+// Line: 8
+
+class X
+{
+       public static void Main ()
+       {
+               var r = nameof (List<int2>);
+       }
+}
\ No newline at end of file
diff --git a/mcs/errors/cs1644-43.cs b/mcs/errors/cs1644-43.cs
new file mode 100644 (file)
index 0000000..c212fe9
--- /dev/null
@@ -0,0 +1,11 @@
+// CS1644: Feature `nameof operator' cannot be used because it is not part of the C# 5.0 language specification
+// Line: 10
+// Compiler options: -langversion:5
+
+class C
+{
+       static void Main ()
+       {
+               var n = nameof (Main);
+       }
+}
\ No newline at end of file
diff --git a/mcs/errors/cs8071-2.cs b/mcs/errors/cs8071-2.cs
new file mode 100644 (file)
index 0000000..6bade94
--- /dev/null
@@ -0,0 +1,13 @@
+// CS8071: Type arguments are not allowed in the nameof operator
+// Line: 10
+
+using SCGL = System.Collections.Generic.List<int>;
+
+class X
+{
+       public static int Main ()
+       {
+               var x = nameof (SCGL.Contains);
+               return 0;
+       }
+}
\ No newline at end of file
diff --git a/mcs/errors/cs8071.cs b/mcs/errors/cs8071.cs
new file mode 100644 (file)
index 0000000..4c0ead2
--- /dev/null
@@ -0,0 +1,18 @@
+// CS8071: Type arguments are not allowed in the nameof operator
+// Line: 16
+
+class G<T>
+{
+       class N
+       {
+               public int Foo;
+       }
+}
+
+class Test
+{
+       public static void Main ()
+       {
+               var n = nameof (G<int>.N.Foo);
+       }
+}
index 8faec94e4c91c02123070165535cd9a22a8bd734..0aa89c0db3cdf1791fbe9163c909f16943542751 100644 (file)
@@ -2031,8 +2031,6 @@ namespace Mono.CSharp {
        }
 
        public class StringConstant : Constant {
-               public readonly string Value;
-
                public StringConstant (BuiltinTypes types, string s, Location loc)
                        : this (types.String, s, loc)
                {
@@ -2047,6 +2045,13 @@ namespace Mono.CSharp {
                        Value = s;
                }
 
+               protected StringConstant (Location loc)
+                       : base (loc)
+               {
+               }
+
+               public string Value { get; protected set; }
+
                public override object GetValue ()
                {
                        return Value;
@@ -2129,6 +2134,113 @@ namespace Mono.CSharp {
                }
        }
 
+       class NameOf : StringConstant
+       {
+               readonly SimpleName name;
+
+               public NameOf (SimpleName name)
+                       : base (name.Location)
+               {
+                       this.name = name;
+               }
+
+               protected override Expression DoResolve (ResolveContext rc)
+               {
+                       throw new NotSupportedException ();
+               }
+
+               bool ResolveArgumentExpression (ResolveContext rc, Expression expr)
+               {
+                       var sn = expr as SimpleName;
+                       if (sn != null) {
+                               Value = sn.Name;
+
+                               if (rc.Module.Compiler.Settings.Version < LanguageVersion.V_6)
+                                       rc.Report.FeatureIsNotAvailable (rc.Module.Compiler, Location, "nameof operator");
+
+                               if (sn.HasTypeArguments) {
+                                       // TODO: csc compatible but unhelpful error message
+                                       rc.Report.Error (1001, loc, "Identifier expected");
+                                       return true;
+                               }
+
+                               sn.LookupNameExpression (rc, MemberLookupRestrictions.IgnoreArity | MemberLookupRestrictions.IgnoreAmbiguity);
+                               return true;
+                       }
+
+                       var ma = expr as MemberAccess;
+                       if (ma != null) {
+                               FullNamedExpression fne = ma.LeftExpression as ATypeNameExpression;
+                               if (fne == null) {
+                                       var qam = ma as QualifiedAliasMember;
+                                       if (qam == null)
+                                               return false;
+
+                                       fne = qam.CreateExpressionFromAlias (rc);
+                                       if (fne == null)
+                                               return true;
+                               }
+
+                               Value = ma.Name;
+
+                               if (rc.Module.Compiler.Settings.Version < LanguageVersion.V_6)
+                                       rc.Report.FeatureIsNotAvailable (rc.Module.Compiler, Location, "nameof operator");
+
+                               if (ma.HasTypeArguments) {
+                                       // TODO: csc compatible but unhelpful error message
+                                       rc.Report.Error (1001, loc, "Identifier expected");
+                                       return true;
+                               }
+                                       
+                               var left = fne.ResolveAsTypeOrNamespace (rc);
+                               if (left == null)
+                                       return true;
+
+                               var ns = left as NamespaceExpression;
+                               if (ns != null) {
+                                       FullNamedExpression retval = ns.LookupTypeOrNamespace (rc, ma.Name, 0, LookupMode.NameOf, loc);
+                                       if (retval == null)
+                                               ns.Error_NamespaceDoesNotExist (rc, ma.Name, 0);
+
+                                       return true;
+                               }
+
+                               if (left.Type.IsGenericOrParentIsGeneric) {
+                                       rc.Report.Error (8071, loc, "Type arguments are not allowed in the nameof operator");
+                               }
+
+                               var mexpr = MemberLookup (rc, false, left.Type, ma.Name, 0, MemberLookupRestrictions.IgnoreArity | MemberLookupRestrictions.IgnoreAmbiguity, loc);
+                               if (mexpr == null) {
+                                       ma.Error_IdentifierNotFound (rc, left.Type);
+                                       return true;
+                               }
+
+                               return true;
+                       }
+
+                       return false;
+               }
+
+               public Expression ResolveOverload (ResolveContext rc, Arguments args)
+               {
+                       if (args == null || args.Count != 1) {
+                               name.Error_NameDoesNotExist (rc);
+                               return null;
+                       }
+
+                       var arg = args [0];
+                       var res = ResolveArgumentExpression (rc, arg.Expr);
+                       if (!res) {
+                               name.Error_NameDoesNotExist (rc);
+                               return null;
+                       }
+
+                       type = rc.BuiltinTypes.String;
+                       eclass = ExprClass.Value;
+                       return this;
+               }
+       }
+
        //
        // Null constant can have its own type, think of `default (Foo)'
        //
index cb69736036b1d0f4e316ee0e82901040efead7d6..d5a0e7ad0e59ef221b0937656642a4f2beb56194 100644 (file)
@@ -21,7 +21,8 @@ namespace Mono.CSharp
        {
                Normal = 0,
                Probing = 1,
-               IgnoreAccessibility = 2
+               IgnoreAccessibility = 2,
+               NameOf = 3
        }
 
        //
index 0d45fe23d291e6bc63c9d45311a1270808b40fcb..d00516ab00b1f18829afd0efb297a182613c3d11 100644 (file)
@@ -791,7 +791,10 @@ namespace Mono.CSharp {
                        None = 0,
                        InvocableOnly = 1,
                        ExactArity = 1 << 2,
-                       ReadAccess = 1 << 3
+                       ReadAccess = 1 << 3,
+                       EmptyArguments = 1 << 4,
+                       IgnoreArity = 1 << 5,
+                       IgnoreAmbiguity = 1 << 6
                }
 
                //
@@ -885,7 +888,7 @@ namespace Mono.CSharp {
                                }
 
                                if (non_method != null) {
-                                       if (ambig_non_method != null && rc != null) {
+                                       if (ambig_non_method != null && rc != null && (restrictions & MemberLookupRestrictions.IgnoreAmbiguity) == 0) {
                                                var report = rc.Module.Compiler.Report;
                                                report.SymbolRelatedToPreviousError (non_method);
                                                report.SymbolRelatedToPreviousError (ambig_non_method);
@@ -2593,6 +2596,11 @@ namespace Mono.CSharp {
                        return SimpleNameResolve (ec, right_side);
                }
 
+               public void Error_NameDoesNotExist (ResolveContext rc)
+               {
+                       rc.Report.Error (103, loc, "The name `{0}' does not exist in the current context", Name);
+               }
+
                protected virtual void Error_TypeOrNamespaceNotFound (IMemberContext ctx)
                {
                        if (ctx.CurrentType != null) {
@@ -2814,6 +2822,9 @@ namespace Mono.CSharp {
                                        return mg;
                                }
 
+                               if (Name == "nameof")
+                                       return new NameOf (this);
+
                                if (errorMode) {
                                        if (variable_found) {
                                                rc.Report.Error (841, loc, "A local variable `{0}' cannot be used before it is declared", Name);
@@ -2859,7 +2870,7 @@ namespace Mono.CSharp {
 
                                                e = rc.LookupNamespaceOrType (Name, -System.Math.Max (1, Arity), LookupMode.Probing, loc);
                                                if (e != null) {
-                                                       if (e.Type.Arity != Arity) {
+                                                       if (e.Type.Arity != Arity && (restrictions & MemberLookupRestrictions.IgnoreArity) == 0) {
                                                                Error_TypeArgumentsCannotBeUsed (rc, e.Type, loc);
                                                                return e;
                                                        }
@@ -2873,7 +2884,7 @@ namespace Mono.CSharp {
                                                        }
                                                }
 
-                                               rc.Report.Error (103, loc, "The name `{0}' does not exist in the current context", Name);
+                                               Error_NameDoesNotExist (rc);
                                        }
 
                                        return ErrorExpression.Instance;
index e387022ef5f880162a90e098af3ec82489ac0f0b..2b46d062c7fa0759c7eb2d1dbbdfae9a6370bb1f 100644 (file)
@@ -6252,8 +6252,14 @@ namespace Mono.CSharp
                        var atn = expr as ATypeNameExpression;
                        if (atn != null) {
                                member_expr = atn.LookupNameExpression (ec, MemberLookupRestrictions.InvocableOnly | MemberLookupRestrictions.ReadAccess);
-                               if (member_expr != null)
+                               if (member_expr != null) {
+                                       var name_of = member_expr as NameOf;
+                                       if (name_of != null) {
+                                               return name_of.ResolveOverload (ec, arguments);
+                                       }
+
                                        member_expr = member_expr.Resolve (ec);
+                               }
                        } else {
                                member_expr = expr.Resolve (ec);
                        }
@@ -8737,22 +8743,30 @@ namespace Mono.CSharp
                        }
                }
 
-               public override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext ec)
+               public FullNamedExpression CreateExpressionFromAlias (IMemberContext mc)
                {
-                       if (alias == GlobalAlias) {
-                               expr = new NamespaceExpression (ec.Module.GlobalRootNamespace, loc);
-                               return base.ResolveAsTypeOrNamespace (ec);
-                       }
+                       if (alias == GlobalAlias)
+                               return new NamespaceExpression (mc.Module.GlobalRootNamespace, loc);
 
-                       int errors = ec.Module.Compiler.Report.Errors;
-                       expr = ec.LookupNamespaceAlias (alias);
+                       int errors = mc.Module.Compiler.Report.Errors;
+                       var expr = mc.LookupNamespaceAlias (alias);
                        if (expr == null) {
-                               if (errors == ec.Module.Compiler.Report.Errors)
-                                       ec.Module.Compiler.Report.Error (432, loc, "Alias `{0}' not found", alias);
+                               if (errors == mc.Module.Compiler.Report.Errors)
+                                       mc.Module.Compiler.Report.Error (432, loc, "Alias `{0}' not found", alias);
+
                                return null;
                        }
-                       
-                       return base.ResolveAsTypeOrNamespace (ec);
+
+                       return expr;
+               }
+
+               public override FullNamedExpression ResolveAsTypeOrNamespace (IMemberContext mc)
+               {
+                       expr = CreateExpressionFromAlias (mc);
+                       if (expr == null)
+                               return null;
+
+                       return base.ResolveAsTypeOrNamespace (mc);
                }
 
                protected override Expression DoResolve (ResolveContext ec)
@@ -9114,7 +9128,7 @@ namespace Mono.CSharp
                                nested = MemberCache.FindNestedType (expr_type, Name, Arity);
                                if (nested == null) {
                                        if (expr_type == tnew_expr) {
-                                               Error_IdentifierNotFound (rc, expr_type, Name);
+                                               Error_IdentifierNotFound (rc, expr_type);
                                                return null;
                                        }
 
@@ -9156,7 +9170,7 @@ namespace Mono.CSharp
                        return texpr;
                }
 
-               protected virtual void Error_IdentifierNotFound (IMemberContext rc, TypeSpec expr_type, string identifier)
+               public void Error_IdentifierNotFound (IMemberContext rc, TypeSpec expr_type)
                {
                        var nested = MemberCache.FindNestedType (expr_type, Name, -System.Math.Max (1, Arity));
 
index 5a368ee05b1a39e73e7708a247bde5c331ad4fa8..6d1873d9915e40e7ae610b5a411e3675e63677bc 100644 (file)
@@ -249,7 +249,7 @@ namespace Mono.CSharp {
                                return null;
 
                        foreach (var ts in found) {
-                               if (ts.Arity == arity) {
+                               if (ts.Arity == arity || mode == LookupMode.NameOf) {
                                        if (best == null) {
                                                if ((ts.Modifiers & Modifiers.INTERNAL) != 0 && !ts.MemberDefinition.IsInternalAsPublic (ctx.Module.DeclaringAssembly) && mode != LookupMode.IgnoreAccessibility)
                                                        continue;
diff --git a/mcs/tests/test-nameof-01.cs b/mcs/tests/test-nameof-01.cs
new file mode 100644 (file)
index 0000000..019d077
--- /dev/null
@@ -0,0 +1,12 @@
+class X
+{
+       public static int Main ()
+       {
+               const string s = nameof (X);
+               System.Console.WriteLine (s);
+               if (s != "X")
+                       return 1;
+
+               return 0;
+       }
+}
\ No newline at end of file
diff --git a/mcs/tests/test-nameof-02.cs b/mcs/tests/test-nameof-02.cs
new file mode 100644 (file)
index 0000000..1d413c6
--- /dev/null
@@ -0,0 +1,127 @@
+using System;
+using System.Collections.Generic;
+using SCG = System.Collections.Generic;
+//using SCGL = System.Collections.Generic.List<>;
+
+class A<T>
+{
+       public class B
+       {
+               public int Foo;
+       }
+}
+
+class X
+{
+       bool field;
+       long Prop { get; set; }
+       event Action ev;
+
+       public static int Main ()
+       {
+               int res;
+               var x = new X ();
+               res = x.SimpleName (1);
+               if (res != 0)
+                       return res;
+
+               res = x.MemberAccess ();
+               if (res != 0)
+                       return 20 + res;
+
+               res = x.QualifiedName ();
+               if (res != 0)
+                       return 40 + res;
+
+               return 0;
+       }
+
+       static void GenMethod<T, U, V> ()
+       {
+       }
+
+       int SimpleName<T> (T arg)
+       {
+               const object c = null;
+               decimal d = 0;
+
+               if (nameof (T) != "T")
+                       return 1;
+
+               if (nameof (arg) != "arg")
+                       return 2;
+
+               if (nameof (c) != "c")
+                       return 3;
+
+               if (nameof (d) != "d")
+                       return 4;
+
+               if (nameof (field) != "field")
+                       return 5;
+
+               if (nameof (Prop) != "Prop")
+                       return 6;
+
+               if (nameof (@Main) != "Main")
+                       return 7;
+
+               if (nameof (ev) != "ev")
+                       return 8;
+
+               if (nameof (Int32) != "Int32")
+                       return 9;
+
+               if (nameof (Action) != "Action")
+                       return 10;
+
+               if (nameof (List) != "List")
+                       return 11;
+
+               if (nameof (GenMethod) != "GenMethod")
+                       return 12;
+
+               return 0;
+       }
+
+       int MemberAccess ()
+       {
+               if (nameof (X.field) != "field")
+                       return 1;
+
+               if (nameof (X.Prop) != "Prop")
+                       return 2;
+
+               if (nameof (Console.WriteLine) != "WriteLine")
+                       return 3;
+
+               if (nameof (System.Collections.Generic.List) != "List")
+                       return 4;
+
+               if (nameof (System.Collections) != "Collections")
+                       return 5;
+
+               if (nameof (X.GenMethod) != "GenMethod")
+                       return 6;
+
+//             A<>.B bb;
+//             if (nameof (A<>.B.Foo) != "B")
+//                     return 7;
+
+               return 0;
+       }
+
+       int QualifiedName ()
+       {
+               if (nameof (global::System.Int32) != "Int32")
+                       return 1;
+
+               if (nameof (SCG.List) != "List")
+                       return 2;
+
+//             if (nameof (SCGL.Contains) != "Contains")
+//                     return 3;
+
+               return 0;
+       }
+}
\ No newline at end of file
diff --git a/mcs/tests/test-nameof-03.cs b/mcs/tests/test-nameof-03.cs
new file mode 100644 (file)
index 0000000..c31084c
--- /dev/null
@@ -0,0 +1,22 @@
+using T2;
+
+static class T2
+{
+       public static int nameof (string s)
+       {
+               return 2;
+       }
+}
+
+class X
+{
+       public static int Main ()
+       {
+               string s = "";
+               var v = nameof (s);
+               if (v != 2)
+                       return 1;
+
+               return 0;
+       }
+}
\ No newline at end of file
diff --git a/mcs/tests/test-nameof-04.cs b/mcs/tests/test-nameof-04.cs
new file mode 100644 (file)
index 0000000..929aa11
--- /dev/null
@@ -0,0 +1,34 @@
+interface IA
+{
+       void M (int arg);
+       int Prop { get; set; }
+}
+
+interface IB
+{
+       void M (string arg);
+       void M<T> (T arg);
+       int Prop { get; set; }
+}
+
+interface I : IA, IB
+{
+       void Extra (string method = nameof (M), string prop = nameof (Prop));
+}
+
+class Ambiguous
+{
+       public static int Main ()
+       {
+               string res;
+               res = nameof (I.M);
+               if (res != "M")
+                       return 1;
+
+               res = nameof (I.Prop);
+               if (res != "Prop")
+                       return 2;
+
+               return 0;
+       }
+}
\ No newline at end of file
index 1802e69467f3dd8ed13df0a366fc74b72117c5a6..da6f88d9cc397cd6a6a6795c4bf311dbc17df585 100644 (file)
       </method>\r
     </type>\r
   </test>\r
+  <test name="test-nameof-01.cs">\r
+    <type name="X">\r
+      <method name="Int32 Main()" attrs="150">\r
+        <size>20</size>\r
+      </method>\r
+      <method name="Void .ctor()" attrs="6278">\r
+        <size>7</size>\r
+      </method>\r
+    </type>\r
+  </test>\r
+  <test name="test-nameof-02.cs">\r
+    <type name="A`1[T]">\r
+      <method name="Void .ctor()" attrs="6278">\r
+        <size>7</size>\r
+      </method>\r
+    </type>\r
+    <type name="A`1+B[T]">\r
+      <method name="Void .ctor()" attrs="6278">\r
+        <size>7</size>\r
+      </method>\r
+    </type>\r
+    <type name="X">\r
+      <method name="Int64 get_Prop()" attrs="2177">\r
+        <size>14</size>\r
+      </method>\r
+      <method name="Void set_Prop(Int64)" attrs="2177">\r
+        <size>8</size>\r
+      </method>\r
+      <method name="Void add_ev(System.Action)" attrs="2177">\r
+        <size>42</size>\r
+      </method>\r
+      <method name="Void remove_ev(System.Action)" attrs="2177">\r
+        <size>42</size>\r
+      </method>\r
+      <method name="Int32 Main()" attrs="150">\r
+        <size>83</size>\r
+      </method>\r
+      <method name="Void GenMethod[T,U,V]()" attrs="145">\r
+        <size>2</size>\r
+      </method>\r
+      <method name="Int32 SimpleName[T](T)" attrs="129">\r
+        <size>17</size>\r
+      </method>\r
+      <method name="Int32 MemberAccess()" attrs="129">\r
+        <size>10</size>\r
+      </method>\r
+      <method name="Int32 QualifiedName()" attrs="129">\r
+        <size>10</size>\r
+      </method>\r
+      <method name="Void .ctor()" attrs="6278">\r
+        <size>7</size>\r
+      </method>\r
+    </type>\r
+  </test>\r
+  <test name="test-nameof-03.cs">\r
+    <type name="T2">\r
+      <method name="Int32 nameof(System.String)" attrs="150">\r
+        <size>10</size>\r
+      </method>\r
+    </type>\r
+    <type name="X">\r
+      <method name="Int32 Main()" attrs="150">\r
+        <size>37</size>\r
+      </method>\r
+      <method name="Void .ctor()" attrs="6278">\r
+        <size>7</size>\r
+      </method>\r
+    </type>\r
+  </test>\r
+  <test name="test-nameof-04.cs">\r
+    <type name="Ambiguous">\r
+      <method name="Int32 Main()" attrs="150">\r
+        <size>68</size>\r
+      </method>\r
+      <method name="Void .ctor()" attrs="6278">\r
+        <size>7</size>\r
+      </method>\r
+    </type>\r
+  </test>\r
   <test name="test-null-operator-01.cs">\r
     <type name="S">\r
       <method name="Int32 get_Prop()" attrs="2182">\r