瀏覽代碼

Add better errors for async

Marek Safar 14 年之前
父節點
當前提交
11b18898e8

+ 13 - 0
mcs/errors/cs0127-2.cs

@@ -0,0 +1,13 @@
+// CS0127: `C.GetValue()': A return keyword must not be followed by any expression when method returns void
+// Line: 11
+
+using System;
+using System.Threading.Tasks;
+
+class C
+{
+	public async void GetValue()
+	{
+		return await Task.FromResult(100);
+	}
+}

+ 1 - 1
mcs/errors/cs0201-2.cs

@@ -1,4 +1,4 @@
-// CS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement
+// CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement
 // Line: 7
 
 class T {

+ 1 - 1
mcs/errors/cs0201-3.cs

@@ -1,4 +1,4 @@
-// CS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement
+// CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement
 // Line: 7
 
 class T {

+ 1 - 1
mcs/errors/cs0201-4.cs

@@ -1,4 +1,4 @@
-// CS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement
+// CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement
 // Line: 8
 
 class MainClass

+ 1 - 1
mcs/errors/cs0201-5.cs

@@ -1,4 +1,4 @@
-// CS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement
+// CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement
 // Line: 10
 
 class TestClass

+ 1 - 1
mcs/errors/cs0201-6.cs

@@ -1,4 +1,4 @@
-// CS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement
+// CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement
 // Line: 13
 
 class C<T>

+ 1 - 1
mcs/errors/cs0201-7.cs

@@ -1,4 +1,4 @@
-// CS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement
+// CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement
 // Line: 10
 
 using System.Linq;

+ 1 - 1
mcs/errors/cs0201-8.cs

@@ -1,4 +1,4 @@
-// CS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement
+// CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement
 // Line: 11
 
 using System;

+ 1 - 1
mcs/errors/cs0201.cs

@@ -1,4 +1,4 @@
-// CS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement
+// CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement
 // Line: 11
 
 using System;

+ 1 - 1
mcs/errors/cs1979.cs

@@ -1,4 +1,4 @@
-// CS1977: Query expression with a source or join sequence of type `dynamic' is not allowed
+// CS1979: Query expressions with a source or join sequence of type `dynamic' are not allowed
 // Line: 11
 
 using System.Linq;

+ 19 - 0
mcs/errors/cs1989-2.cs

@@ -0,0 +1,19 @@
+// CS1989: Async lambda expressions cannot be converted to expression trees
+// Line: 17
+
+using System;
+using System.Linq.Expressions;
+using System.Threading.Tasks;
+
+class C
+{
+	static Task Method ()
+	{
+		return null;
+	}
+	
+	public static void Main ()
+	{
+		Expression<Action<int>> a = async l => Method ();
+	}
+}

+ 1 - 1
mcs/errors/cs1989.cs

@@ -1,4 +1,4 @@
-// CS1989: An expression tree cannot contain an await operator
+// CS1989: Async lambda expressions cannot be converted to expression trees
 // Line: 17
 
 using System;

+ 0 - 13
mcs/errors/cs1992.cs

@@ -1,13 +0,0 @@
-// CS1992: The `await' operator can only be used when its containing method or lambda expression is marked with the `async' modifier
-// Line: 10
-
-using System.Threading.Tasks;
-
-class Tester
-{
-	void Test ()
-	{
-		Task<int> x = null;
-		var a = await x;
-	}
-}

+ 14 - 0
mcs/errors/cs1995.cs

@@ -0,0 +1,14 @@
+// CS1995: The `await' operator may only be used in a query expression within the first collection expression of the initial `from' clause or within the collection expression of a `join' clause
+// Line: 12
+
+using System.Linq;
+using System.Threading.Tasks;
+
+class C
+{
+	public static async void Test ()
+	{
+		Task<int>[] d = null;
+		var r = from x in d select await x;
+	}
+}

+ 1 - 1
mcs/errors/cs1997.cs

@@ -1,4 +1,4 @@
-// CS1997: `C.Test()': A return keyword must not be followed by an expression when async method returns Task. Consider using Task<T>
+// CS1997: `C.Test()': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type
 // Line: 12
 
 using System;

+ 17 - 0
mcs/errors/cs4008.cs

@@ -0,0 +1,17 @@
+// CS4008: Cannot await void method `X.Foo()'. Consider changing method return type to `Task'
+// Line: 10
+
+using System.Threading.Tasks;
+
+class X
+{
+	static async void Test ()
+	{
+		await Foo ();
+	}
+	
+	static async void Foo ()
+	{
+		await Task.FromResult (1);
+	}
+}

+ 22 - 0
mcs/errors/cs4014-2.cs

@@ -0,0 +1,22 @@
+// CS4014: The statement is not awaited and execution of current method continues before the call is completed. Consider using `await' operator
+// Line: 17
+// Compiler options: -warnaserror
+
+using System;
+using System.Threading.Tasks;
+
+class C
+{
+	static Task Method ()
+	{
+		return Task.FromResult (1);
+	}
+	
+	static void TestAsync ()
+	{
+		Func<Task> a = async () => {
+			await Method ();
+			Method ();
+		};
+	}
+}

+ 16 - 0
mcs/errors/cs4014-3.cs

@@ -0,0 +1,16 @@
+// CS4014: The statement is not awaited and execution of current method continues before the call is completed. Consider using `await' operator
+// Line: 18
+// Compiler options: -warnaserror
+
+using System;
+using System.Threading.Tasks;
+
+class C
+{
+	static async Task<int> TestAsync ()
+	{
+		Func<Task> f = null;
+		f ();
+		return await Task.FromResult (2);
+	}
+}

+ 15 - 0
mcs/errors/cs4014-4.cs

@@ -0,0 +1,15 @@
+// CS4014: The statement is not awaited and execution of current method continues before the call is completed. Consider using `await' operator
+// Line: 12
+// Compiler options: -warnaserror
+
+using System;
+using System.Threading.Tasks;
+
+class C
+{
+	static async Task<int> TestAsync ()
+	{
+		new Task (() => {});
+		return await Task.FromResult (2);
+	}
+}

+ 20 - 0
mcs/errors/cs4014.cs

@@ -0,0 +1,20 @@
+// CS4014: The statement is not awaited and execution of current method continues before the call is completed. Consider using `await' operator
+// Line: 18
+// Compiler options: -warnaserror
+
+using System;
+using System.Threading.Tasks;
+
+class C
+{
+	static Task Method ()
+	{
+		return Task.FromResult (1);
+	}
+	
+	static async Task<int> TestAsync ()
+	{
+		Method ();
+		return await Task.FromResult (2);
+	}
+}

+ 14 - 0
mcs/errors/cs4015.cs

@@ -0,0 +1,14 @@
+// CS4015: `C.SynchronousCall(int)': Async methods cannot use `MethodImplOptions.Synchronized'
+// Line: 9
+
+using System.Threading.Tasks;
+using System.Runtime.CompilerServices;
+
+class C
+{
+	[MethodImplAttribute(MethodImplOptions.Synchronized)]
+	public static async Task SynchronousCall (int arg)
+	{
+		await Task.FromResult (1);
+	}
+}

+ 14 - 0
mcs/errors/cs4016.cs

@@ -0,0 +1,14 @@
+// CS4016: `C.GetValue()': The return expression type of async method must be `int' rather than `Task<int>'
+// Line: 12
+
+using System;
+using System.Threading.Tasks;
+
+class C
+{
+	public async Task<int> GetValue()
+	{
+		await Task.FromResult (0);
+		return Task.FromResult (1);
+	}
+}

+ 13 - 0
mcs/errors/cs4033.cs

@@ -0,0 +1,13 @@
+// CS4033: The `await' operator can only be used when its containing method is marked with the `async' modifier
+// Line: 11
+
+using System.Threading.Tasks;
+
+class Tester
+{
+	void Test ()
+	{
+		Task<int> x = null;
+		var a = await x;
+	}
+}

+ 13 - 0
mcs/errors/cs4034.cs

@@ -0,0 +1,13 @@
+// CS4034: The `await' operator can only be used when its containing lambda expression is marked with the `async' modifier
+// Line: 11
+
+using System;
+using System.Threading.Tasks;
+
+class C
+{
+	public void Test ()
+	{
+		Action a = () => await Task.FromResult (1);
+	}
+}

+ 13 - 0
mcs/errors/cs4035.cs

@@ -0,0 +1,13 @@
+// CS4035: The `await' operator can only be used when its containing anonymous method is marked with the `async' modifier
+// Line: 11
+
+using System;
+using System.Threading.Tasks;
+
+class C
+{
+	public void Test ()
+	{
+		Action a = delegate { await Task.FromResult (1); };
+	}
+}

+ 0 - 3
mcs/errors/known-issues-net_4_5

@@ -16,6 +16,3 @@ cs0162-7.cs NO ERROR
 # Operators
 cs0457-2.cs
 cs0457.cs
-
-# all the following are from bug #628673
-cs1979.cs

+ 4 - 0
mcs/mcs/anonymous.cs

@@ -1075,6 +1075,10 @@ namespace Mono.CSharp {
 					} else {
 						int errors = ec.Report.Errors;
 
+						if (Block.IsAsync) {
+							ec.Report.Error (1989, loc, "Async lambda expressions cannot be converted to expression trees");
+						}
+
 						using (ec.Set (ResolveContext.Options.ExpressionTreeConversion)) {
 							am = body.Compatible (ec);
 						}

+ 13 - 6
mcs/mcs/async.cs

@@ -64,11 +64,6 @@ namespace Mono.CSharp
 					"The `await' operator cannot be used in the body of a lock statement");
 			}
 
-			if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) {
-				rc.Report.Error (1989, loc, "An expression tree cannot contain an await operator");
-				return null;
-			}
-
 			if (rc.IsUnsafe) {
 				rc.Report.Error (4004, loc,
 					"The `await' operator cannot be used in an unsafe context");
@@ -134,7 +129,13 @@ namespace Mono.CSharp
 
 			protected override void Error_OperatorCannotBeApplied (ResolveContext rc, TypeSpec type)
 			{
-				rc.Report.Error (4001, loc, "Cannot await `{0}' expression", type.GetSignatureForError ());
+				var invocation = LeftExpression as Invocation;
+				if (invocation != null && invocation.MethodGroup != null && (invocation.MethodGroup.BestCandidate.Modifiers & Modifiers.ASYNC) != 0) {
+					rc.Report.Error (4008, loc, "Cannot await void method `{0}'. Consider changing method return type to `Task'",
+						invocation.GetSignatureForError ());
+				} else {
+					rc.Report.Error (4001, loc, "Cannot await `{0}' expression", type.GetSignatureForError ());
+				}
 			}
 		}
 
@@ -297,6 +298,12 @@ namespace Mono.CSharp
 
 		public override bool Resolve (BlockContext bc)
 		{
+			if (bc.CurrentBlock is Linq.QueryBlock) {
+				bc.Report.Error (1995, loc,
+					"The `await' operator may only be used in a query expression within the first collection expression of the initial `from' clause or within the collection expression of a `join' clause");
+				return false;
+			}
+
 			if (!base.Resolve (bc))
 				return false;
 

+ 6 - 1
mcs/mcs/attribute.cs

@@ -897,6 +897,11 @@ namespace Mono.CSharp {
 		// Returns true for MethodImplAttribute with MethodImplOptions.InternalCall value
 		// 
 		public bool IsInternalCall ()
+		{
+			return (GetMethodImplOptions () & MethodImplOptions.InternalCall) != 0;
+		}
+
+		public MethodImplOptions GetMethodImplOptions ()
 		{
 			MethodImplOptions options = 0;
 			if (pos_args.Count == 1) {
@@ -906,7 +911,7 @@ namespace Mono.CSharp {
 				options = (MethodImplOptions) System.Enum.Parse (typeof (MethodImplOptions), named.GetValue ().ToString ());
 			}
 
-			return (options & MethodImplOptions.InternalCall) != 0;
+			return options;
 		}
 
 		//

+ 1 - 1
mcs/mcs/context.cs

@@ -481,7 +481,7 @@ namespace Mono.CSharp
 			// or it's a parameter
 			//
 			if (CurrentAnonymousMethod is AsyncInitializer)
-				return CurrentBlock.Explicit.HasAwait;
+				return local.IsParameter || CurrentBlock.Explicit.HasAwait;
 
 			return local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
 		}

+ 10 - 2
mcs/mcs/cs-parser.jay

@@ -3760,8 +3760,16 @@ unary_expression
 	| AWAIT prefixed_unary_expression
 	  {
 		if (!async_block) {
-			report.Error (1992, GetLocation ($1),
-				"The `await' operator can only be used when its containing method or lambda expression is marked with the `async' modifier");
+			 if (current_anonymous_method is LambdaExpression) {
+				report.Error (4034, GetLocation ($1),
+					"The `await' operator can only be used when its containing lambda expression is marked with the `async' modifier");
+			} else if (current_anonymous_method is AnonymousMethodExpression) {
+				report.Error (4035, GetLocation ($1),
+					"The `await' operator can only be used when its containing anonymous method is marked with the `async' modifier");
+			} else {
+				report.Error (4033, GetLocation ($1),
+					"The `await' operator can only be used when its containing method is marked with the `async' modifier");
+			}
 		} else {
 			current_block.Explicit.RegisterAsyncAwait ();
 		}

+ 7 - 2
mcs/mcs/ecore.cs

@@ -222,8 +222,7 @@ namespace Mono.CSharp {
 
 		public static void Error_InvalidExpressionStatement (Report Report, Location loc)
 		{
-			Report.Error (201, loc, "Only assignment, call, increment, decrement, and new object " +
-				       "expressions can be used as a statement");
+			Report.Error (201, loc, "Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement");
 		}
 		
 		public void Error_InvalidExpressionStatement (BlockContext ec)
@@ -1025,6 +1024,12 @@ namespace Mono.CSharp {
 			if (es == null)
 				Error_InvalidExpressionStatement (ec);
 
+			if (ec.CurrentAnonymousMethod is AsyncInitializer && !(e is Assign) &&
+				(e.Type.IsGenericTask || e.Type == ec.Module.PredefinedTypes.Task.TypeSpec)) {
+				ec.Report.Warning (4014, 1, e.Location,
+					"The statement is not awaited and execution of current method continues before the call is completed. Consider using `await' operator");
+			}
+
 			return es;
 		}
 

+ 6 - 0
mcs/mcs/expression.cs

@@ -5235,6 +5235,12 @@ namespace Mono.CSharp
 				return expr;
 			}
 		}
+
+		public MethodGroupExpr MethodGroup {
+			get {
+				return mg;
+			}
+		}
 		#endregion
 
 		protected override void CloneTo (CloneContext clonectx, Expression t)

+ 7 - 13
mcs/mcs/linq.cs

@@ -85,6 +85,13 @@ namespace Mono.CSharp.Linq
 				return rmg;
 			}
 
+			protected override Expression DoResolveDynamic (ResolveContext ec, Expression memberExpr)
+			{
+				ec.Report.Error (1979, loc,
+					"Query expressions with a source or join sequence of type `dynamic' are not allowed");
+				return null;
+			}
+
 			#region IErrorHandler Members
 
 			bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
@@ -411,19 +418,6 @@ namespace Mono.CSharp.Linq
 
 		public override Expression BuildQueryClause (ResolveContext ec, Expression lSide, Parameter parameter)
 		{
-/*
-			expr = expr.Resolve (ec);
-			if (expr == null)
-				return null;
-
-			if (expr.Type == InternalType.Dynamic || expr.Type == TypeManager.void_type) {
-				ec.Report.Error (1979, expr.Location,
-					"Query expression with a source or join sequence of type `{0}' is not allowed",
-					TypeManager.CSharpName (expr.Type));
-				return null;
-			}
-*/
-
 			if (IdentifierType != null)
 				expr = CreateCastExpression (expr);
 

+ 7 - 3
mcs/mcs/method.cs

@@ -19,6 +19,7 @@ using System.Security.Permissions;
 using System.Text;
 using System.Linq;
 using Mono.CompilerServices.SymbolWriter;
+using System.Runtime.CompilerServices;
 
 #if NET_2_1
 using XmlElement = System.Object;
@@ -533,10 +534,13 @@ namespace Mono.CSharp {
 			}
 
 			if (a.Type == pa.MethodImpl) {
-				is_external_implementation = a.IsInternalCall ();
-			}
+				if ((ModFlags & Modifiers.ASYNC) != 0 && (a.GetMethodImplOptions () & MethodImplOptions.Synchronized) != 0) {
+					Report.Error (4015, a.Location, "`{0}': Async methods cannot use `MethodImplOptions.Synchronized'",
+						GetSignatureForError ());
+				}
 
-			if (a.Type == pa.DllImport) {
+				is_external_implementation = a.IsInternalCall ();
+			} else if (a.Type == pa.DllImport) {
 				const Modifiers extern_static = Modifiers.EXTERN | Modifiers.STATIC;
 				if ((ModFlags & extern_static) != extern_static) {
 					Report.Error (601, a.Location, "The DllImport attribute must be specified on a method marked `static' and `extern'");

+ 2 - 1
mcs/mcs/report.cs

@@ -55,7 +55,8 @@ namespace Mono.CSharp {
 			2002, 2023, 2029,
 			3000, 3001, 3002, 3003, 3005, 3006, 3007, 3008, 3009,
 			3010, 3011, 3012, 3013, 3014, 3015, 3016, 3017, 3018, 3019,
-			3021, 3022, 3023, 3024, 3026, 3027
+			3021, 3022, 3023, 3024, 3026, 3027,
+			4014
 		};
 
 		static HashSet<int> AllWarningsHashSet;

+ 19 - 2
mcs/mcs/statement.cs

@@ -856,6 +856,8 @@ namespace Mono.CSharp {
 					ec.Report.Error (127, loc,
 						"`{0}': A return keyword must not be followed by any expression when method returns void",
 						ec.GetSignatureForError ());
+
+					return false;
 				}
 			} else {
 				if (am.IsIterator) {
@@ -874,12 +876,21 @@ namespace Mono.CSharp {
 							return true;
 						}
 
+						// TODO: Better error message
+						if (async_type.Kind == MemberKind.Void) {
+							ec.Report.Error (127, loc,
+								"`{0}': A return keyword must not be followed by any expression when method returns void",
+								ec.GetSignatureForError ());
+
+							return false;
+						}
+
 						if (!async_type.IsGenericTask) {
 							if (this is ContextualReturn)
 								return true;
 
 							ec.Report.Error (1997, loc,
-								"`{0}': A return keyword must not be followed by an expression when async method returns Task. Consider using Task<T>",
+								"`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
 								ec.GetSignatureForError ());
 							return false;
 						}
@@ -887,7 +898,13 @@ namespace Mono.CSharp {
 						//
 						// The return type is actually Task<T> type argument
 						//
-						block_return_type = async_type.TypeArguments[0];
+						if (expr.Type == async_type) {
+							ec.Report.Error (4016, loc,
+								"`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
+								ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
+						} else {
+							block_return_type = async_type.TypeArguments[0];
+						}
 					}
 				} else {
 					var l = am as AnonymousMethodBody;

+ 9 - 9
mcs/tests/test-async-05.cs

@@ -1,19 +1,19 @@
-// Compiler options: -langversion:future
-
-using System;
-using System.Linq.Expressions;
 using System.Threading.Tasks;
 
 class C
 {
-	static Task Method ()
+	public async Task SynchronousCall (int arg)
+	{
+		AnotherTask (arg);
+	}
+	
+	Task AnotherTask (int arg)
 	{
-		return null;
+		return Task.FromResult (arg);
 	}
 	
 	public static void Main ()
 	{
-		Expression<Action<int>> a = async l => Method ();
-		a.Compile () (1);
+		new C ().SynchronousCall (1);
 	}
-}
+}

+ 39 - 4
mcs/tests/ver-il-net_4_5.xml

@@ -52816,15 +52816,26 @@
   </test>
   <test name="test-async-05.cs">
     <type name="C">
-      <method name="System.Threading.Tasks.Task Method()" attrs="145">
-        <size>10</size>
-      </method>
       <method name="Void Main()" attrs="150">
-        <size>72</size>
+        <size>14</size>
       </method>
       <method name="Void .ctor()" attrs="6278">
         <size>7</size>
       </method>
+      <method name="System.Threading.Tasks.Task SynchronousCall(Int32)" attrs="134">
+        <size>38</size>
+      </method>
+      <method name="System.Threading.Tasks.Task AnotherTask(Int32)" attrs="129">
+        <size>15</size>
+      </method>
+    </type>
+    <type name="C+&lt;SynchronousCall&gt;c__async0">
+      <method name="Void MoveNext()" attrs="134">
+        <size>49</size>
+      </method>
+      <method name="Void .ctor()" attrs="6278">
+        <size>18</size>
+      </method>
     </type>
   </test>
   <test name="test-async-06.cs">
@@ -55242,6 +55253,30 @@
       </method>
     </type>
   </test>
+  <test name="test-async-28.cs">
+    <type name="C">
+      <method name="System.Threading.Tasks.Task Test()" attrs="150">
+        <size>24</size>
+      </method>
+      <method name="Void Main()" attrs="150">
+        <size>12</size>
+      </method>
+      <method name="Void .ctor()" attrs="6278">
+        <size>7</size>
+      </method>
+    </type>
+    <type name="C+&lt;Test&gt;c__async0">
+      <method name="Void MoveNext()" attrs="134">
+        <size>275</size>
+      </method>
+      <method name="Int32 &lt;&gt;m__0(Int32)" attrs="145">
+        <size>10</size>
+      </method>
+      <method name="Void .ctor()" attrs="6278">
+        <size>18</size>
+      </method>
+    </type>
+  </test>
   <test name="test-cls-00.cs">
     <type name="CLSCLass_6">
       <method name="Void add_Disposed(Delegate)" attrs="2182">