diff --git a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs index 89854549bf12bd374da1694e442d03c01375d320..5e351cc69f8ab376bf4033186874460a29227085 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs @@ -110,6 +110,9 @@ public TypeSymbol InferredReturnType(ref HashSet useSiteDiagnost return _inferredReturnType; } + /// + /// Behavior of this function should be kept aligned with . + /// private static TypeSymbol InferReturnType( BoundBlock block, Binder binder, @@ -148,12 +151,12 @@ public TypeSymbol InferredReturnType(ref HashSet useSiteDiagnost // Otherwise the return type is Task or Task. NamedTypeSymbol taskType = null; var delegateReturnType = delegateType?.GetDelegateType()?.DelegateInvokeMethod?.ReturnType as NamedTypeSymbol; - if ((object)delegateReturnType != null) + if ((object)delegateReturnType != null && delegateReturnType.SpecialType != SpecialType.System_Void) { object builderType; if (delegateReturnType.IsCustomTaskType(out builderType)) { - taskType = delegateReturnType; + taskType = delegateReturnType.ConstructedFrom; } } @@ -176,7 +179,7 @@ public TypeSymbol InferredReturnType(ref HashSet useSiteDiagnost // Some non-void best type T was found; use delegate InvokeMethod // or infer type Task if delegate type not available. var taskTypeT = (object)taskType != null && taskType.Arity == 1 ? - delegateReturnType.ConstructedFrom : + taskType : binder.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task_T); return taskTypeT.Construct(bestResultType); } @@ -294,10 +297,9 @@ internal abstract class UnboundLambdaState { private UnboundLambda _unboundLambda; // we would prefer this readonly, but we have an initialization cycle. protected readonly Binder binder; - private readonly ConcurrentDictionary _bindingCache = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _bindingCache = new ConcurrentDictionary(); - private readonly ConcurrentDictionary _returnInferenceCache = - new ConcurrentDictionary(MemberSignatureComparer.LambdaReturnInferenceCacheComparer); + private readonly ConcurrentDictionary _returnInferenceCache = new ConcurrentDictionary(); private BoundLambda _errorBinding; @@ -424,35 +426,36 @@ private BoundLambda ReallyBind(NamedTypeSymbol delegateType) // when binding for real (not for return inference), there is still // a good chance that we could reuse a body of a lambda previously bound for // return type inference. - MethodSymbol cacheKey = GetCacheKey(delegateType); + var cacheKey = ReturnInferenceCacheKey.Create(delegateType, IsAsync); BoundLambda returnInferenceLambda; - if (_returnInferenceCache.TryGetValue(cacheKey, out returnInferenceLambda) && returnInferenceLambda.InferredFromSingleType && returnInferenceLambda.Symbol.ReturnType == returnType) + if (_returnInferenceCache.TryGetValue(cacheKey, out returnInferenceLambda) && returnInferenceLambda.InferredFromSingleType) { lambdaSymbol = returnInferenceLambda.Symbol; - Debug.Assert(lambdaSymbol.RefKind == refKind); - - lambdaBodyBinder = returnInferenceLambda.Binder; - block = returnInferenceLambda.Body; - diagnostics.AddRange(returnInferenceLambda.Diagnostics); + if ((object)LambdaSymbol.InferenceFailureReturnType != lambdaSymbol.ReturnType && + lambdaSymbol.ReturnType == returnType && lambdaSymbol.RefKind == refKind) + { + lambdaBodyBinder = returnInferenceLambda.Binder; + block = returnInferenceLambda.Body; + diagnostics.AddRange(returnInferenceLambda.Diagnostics); - goto haveLambdaBodyAndBinders; + goto haveLambdaBodyAndBinders; + } } - var parameters = DelegateParameters(invokeMethod); - lambdaSymbol = new LambdaSymbol( binder.Compilation, binder.ContainingMemberOrLambda, _unboundLambda, - parameters, + cacheKey.ParameterTypes, + cacheKey.ParameterRefKinds, refKind, returnType); lambdaBodyBinder = new ExecutableCodeBinder(_unboundLambda.Syntax, lambdaSymbol, ParameterBinder(lambdaSymbol, binder)); block = BindLambdaBody(lambdaSymbol, lambdaBodyBinder, diagnostics); ((ExecutableCodeBinder)lambdaBodyBinder).ValidateIteratorMethods(diagnostics); - ValidateUnsafeParameters(diagnostics, parameters); + ValidateUnsafeParameters(diagnostics, cacheKey.ParameterTypes); haveLambdaBodyAndBinders: @@ -488,28 +491,13 @@ private BoundLambda ReallyBind(NamedTypeSymbol delegateType) SourceMemberMethodSymbol.ReportAsyncParameterErrors(lambdaSymbol.Parameters, diagnostics, lambdaSymbol.Locations[0]); } - // This is an attempt to get a repro for https://devdiv.visualstudio.com/DevDiv/_workitems?id=278481 - if ((object)returnType != null && returnType.SpecialType != SpecialType.System_Void && - !block.HasErrors && !diagnostics.HasAnyResolvedErrors() && block.Statements.Length > 0) - { - BoundStatement first = block.Statements[0]; - if (first.Kind == BoundKind.ReturnStatement) - { - var returnStmt = (BoundReturnStatement)first; - if (returnStmt.ExpressionOpt != null && (object)returnStmt.ExpressionOpt.Type == null) - { - throw ExceptionUtilities.Unreachable; - } - } - } - var result = new BoundLambda(_unboundLambda.Syntax, block, diagnostics.ToReadOnlyAndFree(), lambdaBodyBinder, delegateType, inferReturnType: false) { WasCompilerGenerated = _unboundLambda.WasCompilerGenerated }; return result; } - private void ValidateUnsafeParameters(DiagnosticBag diagnostics, ImmutableArray parameters) + private void ValidateUnsafeParameters(DiagnosticBag diagnostics, ImmutableArray targetParameterTypes) { // It is legal to use a delegate type that has unsafe parameter types inside // a safe context if the anonymous method has no parameter list! @@ -521,13 +509,12 @@ private void ValidateUnsafeParameters(DiagnosticBag diagnostics, ImmutableArray< if (this.HasSignature) { - // NOTE: we can get here with parameters.Length > ParameterCount + // NOTE: we can get here with targetParameterTypes.Length > ParameterCount // in a case where we are binding for error reporting purposes - var numParametersToCheck = Math.Min(parameters.Length, ParameterCount); + var numParametersToCheck = Math.Min(targetParameterTypes.Length, ParameterCount); for (int i = 0; i < numParametersToCheck; i++) { - ParameterSymbol parameter = parameters[i]; - if (parameter.Type.IsUnsafe()) + if (targetParameterTypes[i].IsUnsafe()) { this.binder.ReportUnsafeIfNotAllowed(this.ParameterLocation(i), diagnostics); } @@ -535,12 +522,10 @@ private void ValidateUnsafeParameters(DiagnosticBag diagnostics, ImmutableArray< } } - private BoundLambda ReallyInferReturnType(NamedTypeSymbol delegateType) + private BoundLambda ReallyInferReturnType(NamedTypeSymbol delegateType, ImmutableArray parameterTypes, ImmutableArray parameterRefKinds) { var diagnostics = DiagnosticBag.GetInstance(); - var invokeMethod = DelegateInvokeMethod(delegateType); - var parameters = DelegateParameters(invokeMethod); - var lambdaSymbol = new LambdaSymbol(binder.Compilation, binder.ContainingMemberOrLambda, _unboundLambda, parameters, refKind: Microsoft.CodeAnalysis.RefKind.None, returnType: null); + var lambdaSymbol = new LambdaSymbol(binder.Compilation, binder.ContainingMemberOrLambda, _unboundLambda, parameterTypes, parameterRefKinds, refKind: Microsoft.CodeAnalysis.RefKind.None, returnType: null); Binder lambdaBodyBinder = new ExecutableCodeBinder(_unboundLambda.Syntax, lambdaSymbol, ParameterBinder(lambdaSymbol, binder)); var block = BindLambdaBody(lambdaSymbol, lambdaBodyBinder, diagnostics); @@ -548,7 +533,7 @@ private BoundLambda ReallyInferReturnType(NamedTypeSymbol delegateType) { WasCompilerGenerated = _unboundLambda.WasCompilerGenerated }; HashSet useSiteDiagnostics = null; // TODO: figure out if this should be somehow merged into BoundLambda.Diagnostics. - TypeSymbol returnType = result.InferredReturnType(ref useSiteDiagnostics) ?? new ExtendedErrorTypeSymbol(binder.Compilation, string.Empty, 0, null); + TypeSymbol returnType = result.InferredReturnType(ref useSiteDiagnostics) ?? LambdaSymbol.InferenceFailureReturnType; lambdaSymbol.SetInferredReturnType(result.RefKind, returnType); return result; @@ -556,35 +541,104 @@ private BoundLambda ReallyInferReturnType(NamedTypeSymbol delegateType) public BoundLambda BindForReturnTypeInference(NamedTypeSymbol delegateType) { - MethodSymbol cacheKey = GetCacheKey(delegateType); + var cacheKey = ReturnInferenceCacheKey.Create(delegateType, IsAsync); BoundLambda result; if (!_returnInferenceCache.TryGetValue(cacheKey, out result)) { - result = ReallyInferReturnType(delegateType); + result = ReallyInferReturnType(delegateType, cacheKey.ParameterTypes, cacheKey.ParameterRefKinds); _returnInferenceCache.TryAdd(cacheKey, result); - - // In case the return value of the BoundLambda is distinct from - // delegateType, add the BoundLambda to the cache by the - // distinct signature as well. - _returnInferenceCache.TryAdd(result.Symbol, result); } return result; } - private MethodSymbol GetCacheKey(NamedTypeSymbol delegateType) + /// + /// Behavior of this key should be kept aligned with . + /// + internal class ReturnInferenceCacheKey { - var invoke = DelegateInvokeMethod(delegateType); - if ((object)invoke != null) + public readonly ImmutableArray ParameterTypes; + public readonly ImmutableArray ParameterRefKinds; + public readonly NamedTypeSymbol TaskLikeReturnTypeOpt; + + public static readonly ReturnInferenceCacheKey Empty = new ReturnInferenceCacheKey(ImmutableArray.Empty, ImmutableArray.Empty, null); + + private ReturnInferenceCacheKey(ImmutableArray parameterTypes, ImmutableArray parameterRefKinds, NamedTypeSymbol taskLikeReturnTypeOpt) { - return invoke; + Debug.Assert(parameterTypes.Length == parameterRefKinds.Length); + Debug.Assert((object)taskLikeReturnTypeOpt == null || ((object)taskLikeReturnTypeOpt == taskLikeReturnTypeOpt.ConstructedFrom && taskLikeReturnTypeOpt.IsCustomTaskType(out var builderArgument))); + this.ParameterTypes = parameterTypes; + this.ParameterRefKinds = parameterRefKinds; + this.TaskLikeReturnTypeOpt = taskLikeReturnTypeOpt; } - // delegateType or DelegateInvokeMethod can be null in cases of malformed delegates - // in such case we would want something trivial with no parameters, like a fake static ctor. - // Since the containingType of the .cctor must be non-null, System.Object is used. - return new SynthesizedStaticConstructor(binder.Compilation.GetSpecialType(SpecialType.System_Object)); + public override bool Equals(object obj) + { + if ((object)this == obj) + { + return true; + } + + var other = obj as ReturnInferenceCacheKey; + + return (object)other != null && + other.ParameterTypes.SequenceEqual(this.ParameterTypes) && + other.ParameterRefKinds.SequenceEqual(this.ParameterRefKinds) && + other.TaskLikeReturnTypeOpt == this.TaskLikeReturnTypeOpt; + } + + public override int GetHashCode() + { + return Hash.Combine(TaskLikeReturnTypeOpt?.GetHashCode() ?? 0, Hash.CombineValues(this.ParameterTypes)); + } + + public static ReturnInferenceCacheKey Create(NamedTypeSymbol delegateType, bool isAsync) + { + // delegateType or DelegateInvokeMethod can be null in cases of malformed delegates + // in such case we would want something trivial with no parameters + var parameterTypes = ImmutableArray.Empty; + var parameterRefKinds = ImmutableArray.Empty; + NamedTypeSymbol taskLikeReturnTypeOpt = null; + MethodSymbol invoke = DelegateInvokeMethod(delegateType); + if ((object)invoke != null) + { + int parameterCount = invoke.ParameterCount; + if (parameterCount > 0) + { + var typesBuilder = ArrayBuilder.GetInstance(parameterCount); + var refKindsBuilder = ArrayBuilder.GetInstance(parameterCount); + + foreach (var p in invoke.Parameters) + { + refKindsBuilder.Add(p.RefKind); + typesBuilder.Add(p.Type); + } + + parameterTypes = typesBuilder.ToImmutableAndFree(); + parameterRefKinds = refKindsBuilder.ToImmutableAndFree(); + } + + if (isAsync) + { + var delegateReturnType = invoke.ReturnType as NamedTypeSymbol; + if ((object)delegateReturnType != null && delegateReturnType.SpecialType != SpecialType.System_Void) + { + if (delegateReturnType.IsCustomTaskType(out var builderType)) + { + taskLikeReturnTypeOpt = delegateReturnType.ConstructedFrom; + } + } + } + } + + if (parameterTypes.IsEmpty && parameterRefKinds.IsEmpty && (object)taskLikeReturnTypeOpt == null) + { + return Empty; + } + + return new ReturnInferenceCacheKey(parameterTypes, parameterRefKinds, taskLikeReturnTypeOpt); + } } public TypeSymbol InferReturnType(NamedTypeSymbol delegateType, ref HashSet useSiteDiagnostics) @@ -639,7 +693,7 @@ private BoundLambda ReallyBindForErrorRecovery() return GuessBestBoundLambda(_bindingCache.Values) ?? GuessBestBoundLambda(_returnInferenceCache.Values) - ?? ReallyInferReturnType(null); + ?? ReallyInferReturnType(null, ImmutableArray.Empty, ImmutableArray.Empty); } private static BoundLambda GuessBestBoundLambda(ICollection candidates) diff --git a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs index a382c98d646e55ad01e814876eb6f55c68820f10..c62f662ae3f1b286f1acde7e41bee61c35c2405f 100644 --- a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs @@ -274,9 +274,6 @@ private static Binder GetEnclosingBinder(SyntaxNode node, int position, Binder r { binder = new ExecutableCodeBinder(unexpectedAnonymousFunction, new LambdaSymbol(binder.ContainingMemberOrLambda, - ImmutableArray.Empty, - RefKind.None, - ErrorTypeSymbol.UnknownResultType, unexpectedAnonymousFunction.Kind() == SyntaxKind.AnonymousMethodExpression ? MessageID.IDS_AnonMethod : MessageID.IDS_Lambda, unexpectedAnonymousFunction, isSynthesized: false), diff --git a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs index 8a19b9383600d4933db4984116d4c52ddc192637..cc5cf80b5b67565d17868a0ee3306dc3f4d65bdf 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs @@ -963,12 +963,6 @@ public BoundExpression Null(TypeSymbol type) public BoundTypeExpression Type(TypeSymbol type) { - // This is an attempt to get a repro for https://devdiv.visualstudio.com/DevDiv/_workitems?id=278481 - if ((object)type == null) - { - throw ExceptionUtilities.Unreachable; - } - return new BoundTypeExpression(Syntax, null, type) { WasCompilerGenerated = true }; } diff --git a/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs b/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs index c9273ad4443adaca7c65b074700da7ee3f147c6d..0a87f800588802ab51adc48c75872b6626503ba8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs @@ -262,21 +262,6 @@ internal class MemberSignatureComparer : IEqualityComparer considerRefOutDifference: true, considerCustomModifiers: false); - /// - /// This instance is used as a key in the lambda return type inference. - /// We basically only interested in parameters since inference will set the return type to null. - /// - public static readonly MemberSignatureComparer LambdaReturnInferenceCacheComparer = new MemberSignatureComparer( - considerName: false, // valid invoke is always called "Invoke" - considerExplicitlyImplementedInterfaces: false, - considerReturnType: true, // to differentiate Task types - considerTypeConstraints: false, // valid invoke is never generic - considerCallingConvention: false, // valid invoke is never static - considerRefOutDifference: true, - considerCustomModifiers: true, - ignoreDynamic: false, - ignoreTupleNames: false); - // Compare the "unqualified" part of the member name (no explicit part) private readonly bool _considerName; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs index 1db9a4bbc4be90bc50833185b691add1b267e1c0..e8d44a7b51f8480f835ef2fd33bd814f2fcfae31 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs @@ -18,11 +18,17 @@ internal sealed class LambdaSymbol : MethodSymbol private readonly bool _isSynthesized; private readonly bool _isAsync; + /// + /// This symbol is used as the return type of a LambdaSymbol when we failed to infer its return type. + /// + internal static readonly TypeSymbol InferenceFailureReturnType = new UnsupportedMetadataTypeSymbol(); + public LambdaSymbol( CSharpCompilation compilation, Symbol containingSymbol, UnboundLambda unboundLambda, - ImmutableArray delegateParameters, + ImmutableArray parameterTypes, + ImmutableArray parameterRefKinds, RefKind refKind, TypeSymbol returnType) { @@ -34,14 +40,11 @@ internal sealed class LambdaSymbol : MethodSymbol _isSynthesized = unboundLambda.WasCompilerGenerated; _isAsync = unboundLambda.IsAsync; // No point in making this lazy. We are always going to need these soon after creation of the symbol. - _parameters = MakeParameters(compilation, unboundLambda, delegateParameters); + _parameters = MakeParameters(compilation, unboundLambda, parameterTypes, parameterRefKinds); } public LambdaSymbol( Symbol containingSymbol, - ImmutableArray parameters, - RefKind refKind, - TypeSymbol returnType, MessageID messageID, SyntaxNode syntax, bool isSynthesized) @@ -49,10 +52,10 @@ internal sealed class LambdaSymbol : MethodSymbol _containingSymbol = containingSymbol; _messageID = messageID; _syntax = syntax; - _refKind = refKind; - _returnType = returnType; + _refKind = RefKind.None; + _returnType = ErrorTypeSymbol.UnknownResultType; _isSynthesized = isSynthesized; - _parameters = parameters.SelectAsArray(CopyParameter, this); + _parameters = ImmutableArray.Empty; } public MessageID MessageID { get { return _messageID; } } @@ -287,17 +290,27 @@ public override bool HidesBaseMethodsByName private ImmutableArray MakeParameters( CSharpCompilation compilation, UnboundLambda unboundLambda, - ImmutableArray delegateParameters) + ImmutableArray parameterTypes, + ImmutableArray parameterRefKinds) { + Debug.Assert(parameterTypes.Length == parameterRefKinds.Length); + if (!unboundLambda.HasSignature || unboundLambda.ParameterCount == 0) { // The parameters may be omitted in source, but they are still present on the symbol. - return delegateParameters.SelectAsArray(CopyParameter, this); + return parameterTypes.SelectAsArray((type, ordinal, arg) => + SynthesizedParameterSymbol.Create( + arg.owner, + type, + ordinal, + arg.refKinds[ordinal], + GeneratedNames.LambdaCopyParameterName(ordinal)), // Make sure nothing binds to this. + (owner: this, refKinds: parameterRefKinds)); } var builder = ArrayBuilder.GetInstance(); var hasExplicitlyTypedParameterList = unboundLambda.HasExplicitlyTypedParameterList; - var numDelegateParameters = delegateParameters.IsDefault ? 0 : delegateParameters.Length; + var numDelegateParameters = parameterTypes.Length; for (int p = 0; p < unboundLambda.ParameterCount; ++p) { @@ -316,9 +329,8 @@ public override bool HidesBaseMethodsByName } else if (p < numDelegateParameters) { - ParameterSymbol delegateParameter = delegateParameters[p]; - type = delegateParameter.Type; - refKind = delegateParameter.RefKind; + type = parameterTypes[p]; + refKind = parameterRefKinds[p]; } else { @@ -339,16 +351,6 @@ public override bool HidesBaseMethodsByName return result; } - private static ParameterSymbol CopyParameter(ParameterSymbol parameter, MethodSymbol owner) - { - return SynthesizedParameterSymbol.Create( - owner, - parameter.Type, - parameter.Ordinal, - parameter.RefKind, - GeneratedNames.LambdaCopyParameterName(parameter)); // Make sure nothing binds to this. - } - public sealed override bool Equals(object symbol) { if ((object)this == symbol) return true; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs index dde8915b087c50df388651783539232b6f04ae82..2bb30216ce7f04eb6dd24e7a575fdd9c42c30576 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs @@ -510,9 +510,9 @@ internal static string ReusableHoistedLocalFieldName(int number) return "<>7__wrap" + StringExtensions.GetNumeral(number); } - internal static string LambdaCopyParameterName(ParameterSymbol sourceParameter) + internal static string LambdaCopyParameterName(int ordinal) { - return "<" + sourceParameter.Name + ">"; + return ""; } } } diff --git a/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs b/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs index 0983fced16dbc497aba12bb57df62a677ceb5b4a..71db7fe672e5d97f8fde2305f4aa8e5086ee450e 100644 --- a/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs @@ -1594,7 +1594,7 @@ public static void Main() Assert.Null(GetSymbolNamesJoined(analysis.AlwaysAssigned)); Assert.Equal("p", GetSymbolNamesJoined(analysis.Captured)); Assert.Equal("i", GetSymbolNamesJoined(analysis.UnsafeAddressTaken)); - Assert.Equal("

", GetSymbolNamesJoined(analysis.VariablesDeclared)); + Assert.Equal("", GetSymbolNamesJoined(analysis.VariablesDeclared)); Assert.Equal("p", GetSymbolNamesJoined(analysis.DataFlowsIn)); Assert.Null(GetSymbolNamesJoined(analysis.DataFlowsOut)); @@ -1602,7 +1602,7 @@ public static void Main() Assert.Equal("p", GetSymbolNamesJoined(analysis.ReadInside)); Assert.Equal("i", GetSymbolNamesJoined(analysis.ReadOutside)); - Assert.Equal("

", GetSymbolNamesJoined(analysis.WrittenInside)); + Assert.Equal("", GetSymbolNamesJoined(analysis.WrittenInside)); Assert.Equal("i, p, d", GetSymbolNamesJoined(analysis.WrittenOutside)); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index 4d435b5c894b2b9c7c268374f21b3101e2fec37a..490a646b78d91b6a751bc9cf8a87f16d7497b2b2 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -1638,8 +1638,8 @@ static void Main(string[] args) var lambdaParameters = ((MethodSymbol)(model.GetSymbolInfo(node1)).Symbol).Parameters; - Assert.Equal("System.Object ", lambdaParameters[0].ToTestDisplayString()); - Assert.Equal("System.EventArgs ", lambdaParameters[1].ToTestDisplayString()); + Assert.Equal("System.Object ", lambdaParameters[0].ToTestDisplayString()); + Assert.Equal("System.EventArgs ", lambdaParameters[1].ToTestDisplayString()); CompileAndVerify(compilation); } @@ -2292,7 +2292,7 @@ public static class C } [Fact, WorkItem(278481, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=278481")] - public void LambdaReturningNull() + public void LambdaReturningNull_1() { var src = @" public static class ExtensionMethods @@ -2319,6 +2319,18 @@ public static class ExtensionMethods return null; } + public static System.Collections.Generic.IEnumerable LeftOuterJoin( + this System.Collections.Generic.IEnumerable outerValues, + System.Linq.IQueryable innerValues, + System.Func outerKeySelector, + System.Func innerKeySelector, + System.Func fullResultSelector, + System.Func partialResultSelector) + { + System.Console.WriteLine(""2""); + return null; + } + public static System.Collections.Generic.IEnumerable LeftOuterJoin( this System.Collections.Generic.IEnumerable outerQueryable, System.Collections.Generic.IEnumerable innerQueryable, @@ -2356,6 +2368,36 @@ class B CompileAndVerify(comp, expectedOutput: "1"); } + [Fact, WorkItem(296550, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=296550")] + public void LambdaReturningNull_2() + { + var src = @" +class Test1 + { + public void M1(System.Func x) {} + public void M1(System.Func x) {} + public void M2(System.Func x) {} + public void M2(System.Func x) {} + } + + class Test2 : Test1 + { + void Main() + { + M1(()=> null); + M2(()=> null); + } + } +"; + var comp = CreateCompilationWithMscorlib(src, options: TestOptions.DebugDll); + + comp.VerifyDiagnostics( + // (10,32): error CS1001: Identifier expected + // class Test2 : Test1 + Diagnostic(ErrorCode.ERR_IdentifierExpected, ">").WithLocation(10, 32) + ); + } + [Fact] public void ThrowExpression_Lambda() {