diff --git a/src/Compilers/CSharp/Portable/Binder/LockBinder.cs b/src/Compilers/CSharp/Portable/Binder/LockBinder.cs index fc6cd656013ab..d89afe7cfa6aa 100644 --- a/src/Compilers/CSharp/Portable/Binder/LockBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/LockBinder.cs @@ -78,31 +78,30 @@ internal override BoundStatement BindLockStatementParts(BindingDiagnosticBag dia return new BoundLockStatement(_syntax, expr, stmt, hasErrors); } + // Keep consistent with ISymbolExtensions.TryFindLockTypeInfo. internal static LockTypeInfo? TryFindLockTypeInfo(TypeSymbol lockType, BindingDiagnosticBag diagnostics, SyntaxNode syntax) { - const string LockTypeFullName = "System.Threading.Lock"; - const string EnterLockScopeMethodName = "EnterLockScope"; - const string LockScopeTypeName = "Scope"; + const string LockTypeFullName = $"{nameof(System)}.{nameof(System.Threading)}.{WellKnownMemberNames.LockTypeName}"; - var enterLockScopeMethod = TryFindPublicVoidParameterlessMethod(lockType, EnterLockScopeMethodName); + var enterLockScopeMethod = TryFindPublicVoidParameterlessMethod(lockType, WellKnownMemberNames.EnterLockScopeMethodName); if (enterLockScopeMethod is not { ReturnsVoid: false, RefKind: RefKind.None }) { - Error(diagnostics, ErrorCode.ERR_MissingPredefinedMember, syntax, LockTypeFullName, EnterLockScopeMethodName); + Error(diagnostics, ErrorCode.ERR_MissingPredefinedMember, syntax, LockTypeFullName, WellKnownMemberNames.EnterLockScopeMethodName); return null; } var scopeType = enterLockScopeMethod.ReturnType; - if (!(scopeType is NamedTypeSymbol { Name: LockScopeTypeName, Arity: 0, IsValueType: true, IsRefLikeType: true, DeclaredAccessibility: Accessibility.Public } && - TypeSymbol.Equals(scopeType.ContainingType, lockType, TypeCompareKind.ConsiderEverything))) + if (scopeType is not NamedTypeSymbol { Name: WellKnownMemberNames.LockScopeTypeName, Arity: 0, IsValueType: true, IsRefLikeType: true, DeclaredAccessibility: Accessibility.Public } || + !TypeSymbol.Equals(scopeType.ContainingType, lockType, TypeCompareKind.ConsiderEverything)) { - Error(diagnostics, ErrorCode.ERR_MissingPredefinedMember, syntax, LockTypeFullName, EnterLockScopeMethodName); + Error(diagnostics, ErrorCode.ERR_MissingPredefinedMember, syntax, LockTypeFullName, WellKnownMemberNames.EnterLockScopeMethodName); return null; } var disposeMethod = TryFindPublicVoidParameterlessMethod(scopeType, WellKnownMemberNames.DisposeMethodName); if (disposeMethod is not { ReturnsVoid: true }) { - Error(diagnostics, ErrorCode.ERR_MissingPredefinedMember, syntax, $"{LockTypeFullName}+{LockScopeTypeName}", WellKnownMemberNames.DisposeMethodName); + Error(diagnostics, ErrorCode.ERR_MissingPredefinedMember, syntax, $"{LockTypeFullName}+{WellKnownMemberNames.LockScopeTypeName}", WellKnownMemberNames.DisposeMethodName); return null; } @@ -114,6 +113,7 @@ internal override BoundStatement BindLockStatementParts(BindingDiagnosticBag dia }; } + // Keep consistent with ISymbolExtensions.TryFindPublicVoidParameterlessMethod. private static MethodSymbol? TryFindPublicVoidParameterlessMethod(TypeSymbol type, string name) { var members = type.GetMembers(name); diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 8356729924a94..e3bef07119fac 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -2078,10 +2078,11 @@ internal static bool IsWellKnownTypeIsExternalInit(this TypeSymbol typeSymbol) internal static bool IsWellKnownTypeOutAttribute(this TypeSymbol typeSymbol) => typeSymbol.IsWellKnownInteropServicesTopLevelType("OutAttribute"); + // Keep consistent with ISymbolExtensions.IsWellKnownTypeLock and VB equivalent. internal static bool IsWellKnownTypeLock(this TypeSymbol typeSymbol) { - return typeSymbol is NamedTypeSymbol { Name: "Lock", Arity: 0, ContainingType: null } && - typeSymbol.IsContainedInNamespace("System", "Threading"); + return typeSymbol is NamedTypeSymbol { Name: WellKnownMemberNames.LockTypeName, Arity: 0, ContainingType: null } && + typeSymbol.IsContainedInNamespace(nameof(System), nameof(System.Threading)); } private static bool IsWellKnownInteropServicesTopLevelType(this TypeSymbol typeSymbol, string name) diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/LockTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/LockTests.cs index 5bf87a1a18b68..ba8100faa3a72 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/LockTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/LockTests.cs @@ -817,6 +817,149 @@ public void Dispose() { } Diagnostic(ErrorCode.ERR_BadVisReturnType, "EnterLockScope").WithArguments("System.Threading.Lock.EnterLockScope()", "System.Threading.Lock.Scope").WithLocation(8, 22)); } + [Fact] + public void Obsolete_EnterLockScope() + { + var source = """ + using System; + using System.Threading; + + Lock l = new(); + lock (l) { Console.Write("1"); } + using (l.EnterLockScope()) { Console.Write("2"); } + + namespace System.Threading + { + public class Lock + { + [System.Obsolete] + public Scope EnterLockScope() + { + Console.Write("E"); + return new Scope(); + } + + public ref struct Scope + { + public void Dispose() => Console.Write("D"); + } + } + } + """; + CompileAndVerify(source, expectedOutput: "E1DE2D", verify: Verification.FailsILVerify).VerifyDiagnostics( + // (6,8): warning CS0612: 'Lock.EnterLockScope()' is obsolete + // using (l.EnterLockScope()) { Console.Write("2"); } + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "l.EnterLockScope()").WithArguments("System.Threading.Lock.EnterLockScope()").WithLocation(6, 8)); + } + + [Fact] + public void Obsolete_Lock() + { + var source = """ + using System; + using System.Threading; + + Lock l = new(); + lock (l) { Console.Write("1"); } + using (l.EnterLockScope()) { Console.Write("2"); } + + namespace System.Threading + { + [System.Obsolete] + public class Lock + { + public Scope EnterLockScope() + { + Console.Write("E"); + return new Scope(); + } + + public ref struct Scope + { + public void Dispose() => Console.Write("D"); + } + } + } + """; + CompileAndVerify(source, expectedOutput: "E1DE2D", verify: Verification.FailsILVerify).VerifyDiagnostics( + // (4,1): warning CS0612: 'Lock' is obsolete + // Lock l = new(); + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "Lock").WithArguments("System.Threading.Lock").WithLocation(4, 1)); + } + + [Fact] + public void Obsolete_Scope() + { + var source = """ + using System; + using System.Threading; + + Lock l = new(); + lock (l) { Console.Write("1"); } + using (l.EnterLockScope()) { Console.Write("2"); } + + namespace System.Threading + { + public class Lock + { + public Scope EnterLockScope() + { + Console.Write("E"); + return new Scope(); + } + + [System.Obsolete] + public ref struct Scope + { + public void Dispose() => Console.Write("D"); + } + } + } + """; + CompileAndVerify(source, expectedOutput: "E1DE2D", verify: Verification.FailsILVerify).VerifyDiagnostics( + // (12,16): warning CS0612: 'Lock.Scope' is obsolete + // public Scope EnterLockScope() + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "Scope").WithArguments("System.Threading.Lock.Scope").WithLocation(12, 16), + // (15,24): warning CS0612: 'Lock.Scope' is obsolete + // return new Scope(); + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "Scope").WithArguments("System.Threading.Lock.Scope").WithLocation(15, 24)); + } + + [Fact] + public void Obsolete_Dispose() + { + var source = """ + using System; + using System.Threading; + + Lock l = new(); + lock (l) { Console.Write("1"); } + using (l.EnterLockScope()) { Console.Write("2"); } + + namespace System.Threading + { + public class Lock + { + public Scope EnterLockScope() + { + Console.Write("E"); + return new Scope(); + } + + public ref struct Scope + { + [System.Obsolete] + public void Dispose() => Console.Write("D"); + } + } + } + """; + CompileAndVerify(source, expectedOutput: "E1DE2D", verify: Verification.FailsILVerify).VerifyDiagnostics( + // (6,8): warning CS0612: 'Lock.Scope.Dispose()' is obsolete + // using (l.EnterLockScope()) { Console.Write("2"); } + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "l.EnterLockScope()").WithArguments("System.Threading.Lock.Scope.Dispose()").WithLocation(6, 8)); + } + [Fact] public void GenericLock() { @@ -1489,7 +1632,7 @@ public void EmptyStatement() } [Fact] - public void Nullable() + public void Nullable_01() { var source = """ #nullable enable @@ -1518,6 +1661,37 @@ static void M(Lock? l) Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "l").WithLocation(14, 15)); } + [Fact] + public void Nullable_02() + { + var source = """ + #nullable enable + using System.Threading; + + static class C + { + static void Main() + { + M(new Lock()); + } + + static void M(Lock? l) + { + lock (l) + { + l.EnterLockScope(); + } + } + } + """; + var verifier = CompileAndVerify([source, LockTypeDefinition], verify: Verification.FailsILVerify, + expectedOutput: "EED"); + verifier.VerifyDiagnostics( + // (13,15): warning CS8602: Dereference of a possibly null reference. + // lock (l) + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "l").WithLocation(13, 15)); + } + [Theory, CombinatorialData] public void Null([CombinatorialValues("null", "default")] string expr) { @@ -1742,6 +1916,34 @@ IEnumerable M() Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "new Lock()").WithArguments("System.Threading.Lock.Scope").WithLocation(9, 15)); } + [Fact] + public void Yield_Async() + { + var source = """ + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + + class C + { + async IAsyncEnumerable M() + { + yield return 1; + lock (new Lock()) + { + yield return 2; + } + await Task.Yield(); + yield return 3; + } + } + """; + CreateCompilationWithTasksExtensions([source, LockTypeDefinition, AsyncStreamsTypes]).VerifyDiagnostics( + // (10,15): error CS9215: A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + // lock (new Lock()) + Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithLocation(10, 15)); + } + [Theory, CombinatorialData] public void CastToObject([CombinatorialValues("object ", "dynamic")] string type) { diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ILockStatement.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ILockStatement.cs index a63d47c28e7b1..be0bcabccc9c7 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ILockStatement.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ILockStatement.cs @@ -13,6 +13,21 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { public class IOperationTests_ILockStatement : SemanticModelTestBase { + private const string LockTypeDefinition = """ + namespace System.Threading + { + public class Lock + { + public Scope EnterLockScope() => new Scope(); + + public ref struct Scope + { + public void Dispose() { } + } + } + } + """; + [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void ILockStatement_ObjectLock_FieldReference() @@ -378,6 +393,153 @@ public void M() VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } + [Fact, CompilerTrait(CompilerFeature.IOperation)] + public void ILockStatement_LockObject() + { + var source = """ + using System; + using System.Threading; + + Lock l = new Lock(); + /**/lock (l) + { + }/**/ + """; + + var expectedOperationTree = """ + ILockOperation (OperationKind.Lock, Type: null) (Syntax: 'lock (l) ... }') + Expression: + ILocalReferenceOperation: l (OperationKind.LocalReference, Type: System.Threading.Lock) (Syntax: 'l') + Body: + IBlockOperation (0 statements) (OperationKind.Block, Type: null) (Syntax: '{ ... }') + """; + + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest([source, LockTypeDefinition], expectedOperationTree, expectedDiagnostics); + } + + [Fact, CompilerTrait(CompilerFeature.IOperation)] + public void ILockStatement_LockObject_MissingEnterLockScope() + { + var source = """ + using System; + using System.Threading; + + Lock l = new Lock(); + /**/lock (l) + { + }/**/ + + namespace System.Threading + { + public class Lock { } + } + """; + + var expectedOperationTree = """ + ILockOperation (OperationKind.Lock, Type: null, IsInvalid) (Syntax: 'lock (l) ... }') + Expression: + ILocalReferenceOperation: l (OperationKind.LocalReference, Type: System.Threading.Lock, IsInvalid) (Syntax: 'l') + Body: + IBlockOperation (0 statements) (OperationKind.Block, Type: null) (Syntax: '{ ... }') + """; + + var expectedDiagnostics = new[] + { + // (5,17): error CS0656: Missing compiler required member 'System.Threading.Lock.EnterLockScope' + // /**/lock (l) + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "l").WithArguments("System.Threading.Lock", "EnterLockScope").WithLocation(5, 17) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, CompilerTrait(CompilerFeature.IOperation)] + public void ILockStatement_LockObject_MissingScopeDispose() + { + var source = """ + using System; + using System.Threading; + + Lock l = new Lock(); + /**/lock (l) + { + }/**/ + + namespace System.Threading + { + public class Lock + { + public Scope EnterLockScope() => new Scope(); + + public ref struct Scope { } + } + } + """; + + var expectedOperationTree = """ + ILockOperation (OperationKind.Lock, Type: null, IsInvalid) (Syntax: 'lock (l) ... }') + Expression: + ILocalReferenceOperation: l (OperationKind.LocalReference, Type: System.Threading.Lock, IsInvalid) (Syntax: 'l') + Body: + IBlockOperation (0 statements) (OperationKind.Block, Type: null) (Syntax: '{ ... }') + """; + + var expectedDiagnostics = new[] + { + // (5,17): error CS0656: Missing compiler required member 'System.Threading.Lock+Scope.Dispose' + // /**/lock (l) + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "l").WithArguments("System.Threading.Lock+Scope", "Dispose").WithLocation(5, 17) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, CompilerTrait(CompilerFeature.IOperation)] + public void ILockStatement_LockObject_EnterLockScopeVirtual() + { + var source = """ + using System; + using System.Threading; + + Lock l = new LockDerived(); + /**/lock (l) + { + }/**/ + + namespace System.Threading + { + public class Lock + { + public virtual Scope EnterLockScope() => new(); + + public ref struct Scope + { + public void Dispose() { } + } + } + + public class LockDerived : Lock + { + public override Scope EnterLockScope() => new(); + } + } + """; + + var expectedOperationTree = """ + ILockOperation (OperationKind.Lock, Type: null) (Syntax: 'lock (l) ... }') + Expression: + ILocalReferenceOperation: l (OperationKind.LocalReference, Type: System.Threading.Lock) (Syntax: 'l') + Body: + IBlockOperation (0 statements) (OperationKind.Block, Type: null) (Syntax: '{ ... }') + """; + + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] [Fact] public void LockFlow_01() @@ -1025,5 +1187,966 @@ void M(P input, bool b) VerifyFlowGraphAndDiagnosticsForTest(compilation, expectedGraph, expectedDiagnostics); } + + [Fact, CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + public void LockFlow_LockObject() + { + var source = """ + using System; + using System.Threading; + + class C + { + void M() + /**/{ + Lock l = new Lock(); + lock (l) + { + } + }/**/ + } + """; + + var expectedFlowGraph = """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Threading.Lock l] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Threading.Lock, IsImplicit) (Syntax: 'l = new Lock()') + Left: + ILocalReferenceOperation: l (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Threading.Lock, IsImplicit) (Syntax: 'l = new Lock()') + Right: + IObjectCreationOperation (Constructor: System.Threading.Lock..ctor()) (OperationKind.ObjectCreation, Type: System.Threading.Lock) (Syntax: 'new Lock()') + Arguments(0) + Initializer: + null + Next (Regular) Block[B2] + Entering: {R2} + .locals {R2} + { + CaptureIds: [0] + Block[B2] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'l') + Value: + IInvocationOperation ( System.Threading.Lock.Scope System.Threading.Lock.EnterLockScope()) (OperationKind.Invocation, Type: System.Threading.Lock.Scope, IsImplicit) (Syntax: 'l') + Instance Receiver: + ILocalReferenceOperation: l (OperationKind.LocalReference, Type: System.Threading.Lock) (Syntax: 'l') + Arguments(0) + Next (Regular) Block[B3] + Entering: {R3} {R4} + .try {R3, R4} + { + Block[B3] - Block + Predecessors: [B2] + Statements (0) + Next (Regular) Block[B5] + Finalizing: {R5} + Leaving: {R4} {R3} {R2} {R1} + } + .finally {R5} + { + Block[B4] - Block + Predecessors (0) + Statements (1) + IInvocationOperation ( void System.Threading.Lock.Scope.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'l') + Instance Receiver: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Threading.Lock.Scope, IsImplicit) (Syntax: 'l') + Arguments(0) + Next (StructuredExceptionHandling) Block[null] + } + } + } + Block[B5] - Exit + Predecessors: [B3] + Statements (0) + """; + + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest([source, LockTypeDefinition], expectedFlowGraph, expectedDiagnostics); + } + + [Fact, CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + public void LockFlow_LockObject_NonEmptyBody() + { + var source = """ + using System; + using System.Threading; + + class C + { + void M() + /**/{ + Lock l = new Lock(); + lock (l) + { + Console.Write("Body"); + } + }/**/ + } + """; + + var expectedFlowGraph = """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Threading.Lock l] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Threading.Lock, IsImplicit) (Syntax: 'l = new Lock()') + Left: + ILocalReferenceOperation: l (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Threading.Lock, IsImplicit) (Syntax: 'l = new Lock()') + Right: + IObjectCreationOperation (Constructor: System.Threading.Lock..ctor()) (OperationKind.ObjectCreation, Type: System.Threading.Lock) (Syntax: 'new Lock()') + Arguments(0) + Initializer: + null + Next (Regular) Block[B2] + Entering: {R2} + .locals {R2} + { + CaptureIds: [0] + Block[B2] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'l') + Value: + IInvocationOperation ( System.Threading.Lock.Scope System.Threading.Lock.EnterLockScope()) (OperationKind.Invocation, Type: System.Threading.Lock.Scope, IsImplicit) (Syntax: 'l') + Instance Receiver: + ILocalReferenceOperation: l (OperationKind.LocalReference, Type: System.Threading.Lock) (Syntax: 'l') + Arguments(0) + Next (Regular) Block[B3] + Entering: {R3} {R4} + .try {R3, R4} + { + Block[B3] - Block + Predecessors: [B2] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'Console.Write("Body");') + Expression: + IInvocationOperation (void System.Console.Write(System.String value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Console.Write("Body")') + Instance Receiver: + null + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: '"Body"') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Body") (Syntax: '"Body"') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Next (Regular) Block[B5] + Finalizing: {R5} + Leaving: {R4} {R3} {R2} {R1} + } + .finally {R5} + { + Block[B4] - Block + Predecessors (0) + Statements (1) + IInvocationOperation ( void System.Threading.Lock.Scope.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'l') + Instance Receiver: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Threading.Lock.Scope, IsImplicit) (Syntax: 'l') + Arguments(0) + Next (StructuredExceptionHandling) Block[null] + } + } + } + Block[B5] - Exit + Predecessors: [B3] + Statements (0) + """; + + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest([source, LockTypeDefinition], expectedFlowGraph, expectedDiagnostics); + } + + [Fact, CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + public void LockFlow_LockObject_ConditionalBody() + { + var source = """ + using System; + using System.Threading; + + class C + { + void M(bool b) + /**/{ + Lock l = new Lock(); + lock (l) + { + if (b) + { + Console.Write("Body"); + } + else + { + Console.Write("Else"); + } + } + }/**/ + } + """; + + var expectedFlowGraph = """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Threading.Lock l] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Threading.Lock, IsImplicit) (Syntax: 'l = new Lock()') + Left: + ILocalReferenceOperation: l (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Threading.Lock, IsImplicit) (Syntax: 'l = new Lock()') + Right: + IObjectCreationOperation (Constructor: System.Threading.Lock..ctor()) (OperationKind.ObjectCreation, Type: System.Threading.Lock) (Syntax: 'new Lock()') + Arguments(0) + Initializer: + null + Next (Regular) Block[B2] + Entering: {R2} + .locals {R2} + { + CaptureIds: [0] + Block[B2] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'l') + Value: + IInvocationOperation ( System.Threading.Lock.Scope System.Threading.Lock.EnterLockScope()) (OperationKind.Invocation, Type: System.Threading.Lock.Scope, IsImplicit) (Syntax: 'l') + Instance Receiver: + ILocalReferenceOperation: l (OperationKind.LocalReference, Type: System.Threading.Lock) (Syntax: 'l') + Arguments(0) + Next (Regular) Block[B3] + Entering: {R3} {R4} + .try {R3, R4} + { + Block[B3] - Block + Predecessors: [B2] + Statements (0) + Jump if False (Regular) to Block[B5] + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Next (Regular) Block[B4] + Block[B4] - Block + Predecessors: [B3] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'Console.Write("Body");') + Expression: + IInvocationOperation (void System.Console.Write(System.String value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Console.Write("Body")') + Instance Receiver: + null + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: '"Body"') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Body") (Syntax: '"Body"') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Next (Regular) Block[B7] + Finalizing: {R5} + Leaving: {R4} {R3} {R2} {R1} + Block[B5] - Block + Predecessors: [B3] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'Console.Write("Else");') + Expression: + IInvocationOperation (void System.Console.Write(System.String value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Console.Write("Else")') + Instance Receiver: + null + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: '"Else"') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Else") (Syntax: '"Else"') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Next (Regular) Block[B7] + Finalizing: {R5} + Leaving: {R4} {R3} {R2} {R1} + } + .finally {R5} + { + Block[B6] - Block + Predecessors (0) + Statements (1) + IInvocationOperation ( void System.Threading.Lock.Scope.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'l') + Instance Receiver: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Threading.Lock.Scope, IsImplicit) (Syntax: 'l') + Arguments(0) + Next (StructuredExceptionHandling) Block[null] + } + } + } + Block[B7] - Exit + Predecessors: [B4] [B5] + Statements (0) + """; + + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest([source, LockTypeDefinition], expectedFlowGraph, expectedDiagnostics); + } + + [Fact, CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + public void LockFlow_LockObject_Conditional() + { + var source = """ + using System; + using System.Threading; + + class C + { + void M(bool b, Lock l1, Lock l2) + /**/{ + lock (b ? l1 : l2) + { + } + }/**/ + } + """; + + var expectedFlowGraph = """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + .locals {R1} + { + CaptureIds: [1] + .locals {R2} + { + CaptureIds: [0] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Jump if False (Regular) to Block[B3] + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Next (Regular) Block[B2] + Block[B2] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'l1') + Value: + IParameterReferenceOperation: l1 (OperationKind.ParameterReference, Type: System.Threading.Lock) (Syntax: 'l1') + Next (Regular) Block[B4] + Block[B3] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'l2') + Value: + IParameterReferenceOperation: l2 (OperationKind.ParameterReference, Type: System.Threading.Lock) (Syntax: 'l2') + Next (Regular) Block[B4] + Block[B4] - Block + Predecessors: [B2] [B3] + Statements (1) + IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'b ? l1 : l2') + Value: + IInvocationOperation ( System.Threading.Lock.Scope System.Threading.Lock.EnterLockScope()) (OperationKind.Invocation, Type: System.Threading.Lock.Scope, IsImplicit) (Syntax: 'b ? l1 : l2') + Instance Receiver: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Threading.Lock, IsImplicit) (Syntax: 'b ? l1 : l2') + Arguments(0) + Next (Regular) Block[B5] + Leaving: {R2} + Entering: {R3} {R4} + } + .try {R3, R4} + { + Block[B5] - Block + Predecessors: [B4] + Statements (0) + Next (Regular) Block[B7] + Finalizing: {R5} + Leaving: {R4} {R3} {R1} + } + .finally {R5} + { + Block[B6] - Block + Predecessors (0) + Statements (1) + IInvocationOperation ( void System.Threading.Lock.Scope.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'b ? l1 : l2') + Instance Receiver: + IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Threading.Lock.Scope, IsImplicit) (Syntax: 'b ? l1 : l2') + Arguments(0) + Next (StructuredExceptionHandling) Block[null] + } + } + Block[B7] - Exit + Predecessors: [B5] + Statements (0) + """; + + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest([source, LockTypeDefinition], expectedFlowGraph, expectedDiagnostics); + } + + [Fact, CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + public void LockFlow_LockObject_Conditional_Object() + { + var source = """ + using System; + using System.Threading; + + class C + { + void M(bool b, object o) + /**/{ + Lock l = new Lock(); + lock (b ? l : o) + { + } + }/**/ + } + """; + + var expectedFlowGraph = """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Threading.Lock l] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Threading.Lock, IsImplicit) (Syntax: 'l = new Lock()') + Left: + ILocalReferenceOperation: l (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Threading.Lock, IsImplicit) (Syntax: 'l = new Lock()') + Right: + IObjectCreationOperation (Constructor: System.Threading.Lock..ctor()) (OperationKind.ObjectCreation, Type: System.Threading.Lock) (Syntax: 'new Lock()') + Arguments(0) + Initializer: + null + Next (Regular) Block[B2] + Entering: {R2} + .locals {R2} + { + Locals: [System.Boolean ?] + CaptureIds: [0] + Block[B2] - Block + Predecessors: [B1] + Statements (0) + Jump if False (Regular) to Block[B4] + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Next (Regular) Block[B3] + Block[B3] - Block + Predecessors: [B2] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'l') + Value: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'l') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + (ImplicitReference) + Operand: + ILocalReferenceOperation: l (OperationKind.LocalReference, Type: System.Threading.Lock) (Syntax: 'l') + Next (Regular) Block[B5] + Entering: {R3} {R4} + Block[B4] - Block + Predecessors: [B2] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'o') + Value: + IParameterReferenceOperation: o (OperationKind.ParameterReference, Type: System.Object) (Syntax: 'o') + Next (Regular) Block[B5] + Entering: {R3} {R4} + .try {R3, R4} + { + Block[B5] - Block + Predecessors: [B3] [B4] + Statements (1) + IInvocationOperation (void System.Threading.Monitor.Enter(System.Object obj, ref System.Boolean lockTaken)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'b ? l : o') + Instance Receiver: + null + Arguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: obj) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'b ? l : o') + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Object, IsImplicit) (Syntax: 'b ? l : o') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: lockTaken) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'b ? l : o') + ILocalReferenceOperation: (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Boolean, IsImplicit) (Syntax: 'b ? l : o') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Next (Regular) Block[B9] + Finalizing: {R5} + Leaving: {R4} {R3} {R2} {R1} + } + .finally {R5} + { + Block[B6] - Block + Predecessors (0) + Statements (0) + Jump if False (Regular) to Block[B8] + ILocalReferenceOperation: (OperationKind.LocalReference, Type: System.Boolean, IsImplicit) (Syntax: 'b ? l : o') + Next (Regular) Block[B7] + Block[B7] - Block + Predecessors: [B6] + Statements (1) + IInvocationOperation (void System.Threading.Monitor.Exit(System.Object obj)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'b ? l : o') + Instance Receiver: + null + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: obj) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'b ? l : o') + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Object, IsImplicit) (Syntax: 'b ? l : o') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Next (Regular) Block[B8] + Block[B8] - Block + Predecessors: [B6] [B7] + Statements (0) + Next (StructuredExceptionHandling) Block[null] + } + } + } + Block[B9] - Exit + Predecessors: [B5] + Statements (0) + """; + + var expectedDiagnostics = new[] + { + // (9,19): warning CS9214: A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + // lock (b ? l : o) + Diagnostic(ErrorCode.WRN_ConvertingLock, "l").WithLocation(9, 19) + }; + + VerifyFlowGraphAndDiagnosticsForTest([source, LockTypeDefinition], expectedFlowGraph, expectedDiagnostics); + } + + [Fact, CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + public void LockFlow_LockObject_Coalesce() + { + var source = """ + using System; + using System.Threading; + + class C + { + void M(Lock l1, Lock l2) + /**/{ + lock (l1 ?? l2) + { + } + }/**/ + } + """; + + var expectedFlowGraph = """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} {R3} + .locals {R1} + { + CaptureIds: [2] + .locals {R2} + { + CaptureIds: [1] + .locals {R3} + { + CaptureIds: [0] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'l1') + Value: + IParameterReferenceOperation: l1 (OperationKind.ParameterReference, Type: System.Threading.Lock) (Syntax: 'l1') + Jump if True (Regular) to Block[B3] + IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'l1') + Operand: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Threading.Lock, IsImplicit) (Syntax: 'l1') + Leaving: {R3} + Next (Regular) Block[B2] + Block[B2] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'l1') + Value: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Threading.Lock, IsImplicit) (Syntax: 'l1') + Next (Regular) Block[B4] + Leaving: {R3} + } + Block[B3] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'l2') + Value: + IParameterReferenceOperation: l2 (OperationKind.ParameterReference, Type: System.Threading.Lock) (Syntax: 'l2') + Next (Regular) Block[B4] + Block[B4] - Block + Predecessors: [B2] [B3] + Statements (1) + IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'l1 ?? l2') + Value: + IInvocationOperation ( System.Threading.Lock.Scope System.Threading.Lock.EnterLockScope()) (OperationKind.Invocation, Type: System.Threading.Lock.Scope, IsImplicit) (Syntax: 'l1 ?? l2') + Instance Receiver: + IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Threading.Lock, IsImplicit) (Syntax: 'l1 ?? l2') + Arguments(0) + Next (Regular) Block[B5] + Leaving: {R2} + Entering: {R4} {R5} + } + .try {R4, R5} + { + Block[B5] - Block + Predecessors: [B4] + Statements (0) + Next (Regular) Block[B7] + Finalizing: {R6} + Leaving: {R5} {R4} {R1} + } + .finally {R6} + { + Block[B6] - Block + Predecessors (0) + Statements (1) + IInvocationOperation ( void System.Threading.Lock.Scope.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'l1 ?? l2') + Instance Receiver: + IFlowCaptureReferenceOperation: 2 (OperationKind.FlowCaptureReference, Type: System.Threading.Lock.Scope, IsImplicit) (Syntax: 'l1 ?? l2') + Arguments(0) + Next (StructuredExceptionHandling) Block[null] + } + } + Block[B7] - Exit + Predecessors: [B5] + Statements (0) + """; + + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest([source, LockTypeDefinition], expectedFlowGraph, expectedDiagnostics); + } + + [Fact, CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + public void LockFlow_LockObject_MissingEnterLockScope() + { + var source = """ + using System; + using System.Threading; + + class C + { + void M() + /**/{ + Lock l = new Lock(); + lock (l) + { + } + }/**/ + } + + namespace System.Threading + { + public class Lock { } + } + """; + + var expectedFlowGraph = """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Threading.Lock l] + Block[B1] - Block + Predecessors: [B0] + Statements (2) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Threading.Lock, IsImplicit) (Syntax: 'l = new Lock()') + Left: + ILocalReferenceOperation: l (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Threading.Lock, IsImplicit) (Syntax: 'l = new Lock()') + Right: + IObjectCreationOperation (Constructor: System.Threading.Lock..ctor()) (OperationKind.ObjectCreation, Type: System.Threading.Lock) (Syntax: 'new Lock()') + Arguments(0) + Initializer: + null + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid) (Syntax: 'lock (l) ... }') + Expression: + IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid, IsImplicit) (Syntax: 'l') + Children(1): + ILocalReferenceOperation: l (OperationKind.LocalReference, Type: System.Threading.Lock, IsInvalid) (Syntax: 'l') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """; + + var expectedDiagnostics = new[] + { + // (9,15): error CS0656: Missing compiler required member 'System.Threading.Lock.EnterLockScope' + // lock (l) + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "l").WithArguments("System.Threading.Lock", "EnterLockScope").WithLocation(9, 15) + }; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); + } + + [Fact, CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + public void LockFlow_LockObject_MissingEnterLockScope_NonEmptyBody() + { + var source = """ + using System; + using System.Threading; + + class C + { + void M() + /**/{ + Lock l = new Lock(); + lock (l) + { + Console.Write("Body"); + } + }/**/ + } + + namespace System.Threading + { + public class Lock { } + } + """; + + var expectedFlowGraph = """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Threading.Lock l] + Block[B1] - Block + Predecessors: [B0] + Statements (3) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Threading.Lock, IsImplicit) (Syntax: 'l = new Lock()') + Left: + ILocalReferenceOperation: l (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Threading.Lock, IsImplicit) (Syntax: 'l = new Lock()') + Right: + IObjectCreationOperation (Constructor: System.Threading.Lock..ctor()) (OperationKind.ObjectCreation, Type: System.Threading.Lock) (Syntax: 'new Lock()') + Arguments(0) + Initializer: + null + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid) (Syntax: 'lock (l) ... }') + Expression: + IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid, IsImplicit) (Syntax: 'l') + Children(1): + ILocalReferenceOperation: l (OperationKind.LocalReference, Type: System.Threading.Lock, IsInvalid) (Syntax: 'l') + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'Console.Write("Body");') + Expression: + IInvocationOperation (void System.Console.Write(System.String value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Console.Write("Body")') + Instance Receiver: + null + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: '"Body"') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Body") (Syntax: '"Body"') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """; + + var expectedDiagnostics = new[] + { + // (9,15): error CS0656: Missing compiler required member 'System.Threading.Lock.EnterLockScope' + // lock (l) + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "l").WithArguments("System.Threading.Lock", "EnterLockScope").WithLocation(9, 15) + }; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); + } + + [Fact, CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + public void LockFlow_LockObject_MissingScopeDispose() + { + var source = """ + using System; + using System.Threading; + + class C + { + void M() + /**/{ + Lock l = new Lock(); + lock (l) + { + } + }/**/ + } + + namespace System.Threading + { + public class Lock + { + public Scope EnterLockScope() => new Scope(); + + public ref struct Scope { } + } + } + """; + + var expectedFlowGraph = """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Threading.Lock l] + Block[B1] - Block + Predecessors: [B0] + Statements (2) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Threading.Lock, IsImplicit) (Syntax: 'l = new Lock()') + Left: + ILocalReferenceOperation: l (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Threading.Lock, IsImplicit) (Syntax: 'l = new Lock()') + Right: + IObjectCreationOperation (Constructor: System.Threading.Lock..ctor()) (OperationKind.ObjectCreation, Type: System.Threading.Lock) (Syntax: 'new Lock()') + Arguments(0) + Initializer: + null + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid) (Syntax: 'lock (l) ... }') + Expression: + IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid, IsImplicit) (Syntax: 'l') + Children(1): + ILocalReferenceOperation: l (OperationKind.LocalReference, Type: System.Threading.Lock, IsInvalid) (Syntax: 'l') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """; + + var expectedDiagnostics = new[] + { + // (9,15): error CS0656: Missing compiler required member 'System.Threading.Lock+Scope.Dispose' + // lock (l) + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "l").WithArguments("System.Threading.Lock+Scope", "Dispose").WithLocation(9, 15) + }; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); + } + + [Fact, CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + public void LockFlow_LockObject_EnterLockScopeVirtual() + { + var source = """ + using System; + using System.Threading; + + class C + { + void M() + /**/{ + Lock l = new LockDerived(); + lock (l) + { + } + }/**/ + } + + namespace System.Threading + { + public class Lock + { + public virtual Scope EnterLockScope() => new(); + + public ref struct Scope + { + public void Dispose() { } + } + } + + public class LockDerived : Lock + { + public override Scope EnterLockScope() => new(); + } + } + """; + + var expectedFlowGraph = """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Threading.Lock l] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Threading.Lock, IsImplicit) (Syntax: 'l = new LockDerived()') + Left: + ILocalReferenceOperation: l (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Threading.Lock, IsImplicit) (Syntax: 'l = new LockDerived()') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Threading.Lock, IsImplicit) (Syntax: 'new LockDerived()') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + (ImplicitReference) + Operand: + IObjectCreationOperation (Constructor: System.Threading.LockDerived..ctor()) (OperationKind.ObjectCreation, Type: System.Threading.LockDerived) (Syntax: 'new LockDerived()') + Arguments(0) + Initializer: + null + Next (Regular) Block[B2] + Entering: {R2} + .locals {R2} + { + CaptureIds: [0] + Block[B2] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'l') + Value: + IInvocationOperation (virtual System.Threading.Lock.Scope System.Threading.Lock.EnterLockScope()) (OperationKind.Invocation, Type: System.Threading.Lock.Scope, IsImplicit) (Syntax: 'l') + Instance Receiver: + ILocalReferenceOperation: l (OperationKind.LocalReference, Type: System.Threading.Lock) (Syntax: 'l') + Arguments(0) + Next (Regular) Block[B3] + Entering: {R3} {R4} + .try {R3, R4} + { + Block[B3] - Block + Predecessors: [B2] + Statements (0) + Next (Regular) Block[B5] + Finalizing: {R5} + Leaving: {R4} {R3} {R2} {R1} + } + .finally {R5} + { + Block[B4] - Block + Predecessors (0) + Statements (1) + IInvocationOperation ( void System.Threading.Lock.Scope.Dispose()) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'l') + Instance Receiver: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Threading.Lock.Scope, IsImplicit) (Syntax: 'l') + Arguments(0) + Next (StructuredExceptionHandling) Block[null] + } + } + } + Block[B5] - Exit + Predecessors: [B3] + Statements (0) + """; + + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); + } } } diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index b5b3ae0dfaad2..c503859017400 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -3946,7 +3946,8 @@ private void LinkThrowStatement(IOperation? exception) return FinishVisitingStatement(operation); } - private void HandleUsingOperationParts(IOperation resources, IOperation body, IMethodSymbol? disposeMethod, ImmutableArray disposeArguments, ImmutableArray locals, bool isAsynchronous) + private void HandleUsingOperationParts(IOperation resources, IOperation body, IMethodSymbol? disposeMethod, ImmutableArray disposeArguments, ImmutableArray locals, bool isAsynchronous, + Func? visitResource = null) { var usingRegion = new RegionBuilder(ControlFlowRegionKind.LocalLifetime, locals: locals); EnterRegion(usingRegion); @@ -3957,6 +3958,8 @@ private void HandleUsingOperationParts(IOperation resources, IOperation body, IM if (resources is IVariableDeclarationGroupOperation declarationGroup) { + Debug.Assert(visitResource is null); + var resourceQueue = ArrayBuilder<(IVariableDeclarationOperation, IVariableDeclaratorOperation)>.GetInstance(declarationGroup.Declarations.Length); foreach (IVariableDeclarationOperation declaration in declarationGroup.Declarations) @@ -3977,7 +3980,7 @@ private void HandleUsingOperationParts(IOperation resources, IOperation body, IM Debug.Assert(resources.Kind != OperationKind.VariableDeclarator); EvalStackFrame frame = PushStackFrame(); - IOperation resource = VisitRequired(resources); + IOperation resource = visitResource != null ? visitResource(resources) : VisitRequired(resources); if (shouldConvertToIDisposableBeforeTry(resource)) { @@ -4200,6 +4203,57 @@ private IOperation ConvertToIDisposable(IOperation operand, ITypeSymbol iDisposa { StartVisitingStatement(operation); + // `lock (l) { }` on value of type `System.Threading.Lock` is lowered to `using (l.EnterLockScope()) { }`. + if (operation.LockedValue.Type?.IsWellKnownTypeLock() == true) + { + if (operation.LockedValue.Type.TryFindLockTypeInfo() is { } lockTypeInfo) + { + HandleUsingOperationParts( + resources: operation.LockedValue, + body: operation.Body, + disposeMethod: lockTypeInfo.ScopeDisposeMethod, + disposeArguments: ImmutableArray.Empty, + locals: ImmutableArray.Empty, + isAsynchronous: false, + visitResource: (resource) => + { + var lockObject = VisitRequired(resource); + + return new InvocationOperation( + targetMethod: lockTypeInfo.EnterLockScopeMethod, + constrainedToType: null, + instance: lockObject, + isVirtual: lockTypeInfo.EnterLockScopeMethod.IsVirtual || + lockTypeInfo.EnterLockScopeMethod.IsAbstract || + lockTypeInfo.EnterLockScopeMethod.IsOverride, + arguments: ImmutableArray.Empty, + semanticModel: null, + syntax: lockObject.Syntax, + type: lockTypeInfo.EnterLockScopeMethod.ReturnType, + isImplicit: true); + }); + + return FinishVisitingStatement(operation); + } + else + { + IOperation? underlying = Visit(operation.LockedValue); + + if (underlying is not null) + { + AddStatement(new ExpressionStatementOperation( + MakeInvalidOperation(type: null, underlying), + semanticModel: null, + operation.Syntax, + IsImplicit(operation))); + } + + VisitStatement(operation.Body); + + return FinishVisitingStatement(operation); + } + } + ITypeSymbol objectType = _compilation.GetSpecialType(SpecialType.System_Object); // If Monitor.Enter(object, ref bool) is available: diff --git a/src/Compilers/Core/Portable/Symbols/ISymbolExtensions.cs b/src/Compilers/Core/Portable/Symbols/ISymbolExtensions.cs index d1861841feac0..159ac88ed51c0 100644 --- a/src/Compilers/Core/Portable/Symbols/ISymbolExtensions.cs +++ b/src/Compilers/Core/Portable/Symbols/ISymbolExtensions.cs @@ -6,6 +6,8 @@ namespace Microsoft.CodeAnalysis { + using LockTypeInfo = (IMethodSymbol EnterLockScopeMethod, IMethodSymbol ScopeDisposeMethod); + public static partial class ISymbolExtensions { /// @@ -116,5 +118,83 @@ internal static bool IsInSource(this ISymbol symbol) return false; } + + // Keep consistent with TypeSymbolExtensions.IsWellKnownTypeLock. + internal static bool IsWellKnownTypeLock(this ITypeSymbol type) + { + return type is INamedTypeSymbol + { + Name: WellKnownMemberNames.LockTypeName, + Arity: 0, + ContainingType: null, + ContainingNamespace: + { + Name: nameof(System.Threading), + ContainingNamespace: + { + Name: nameof(System), + ContainingNamespace.IsGlobalNamespace: true, + } + } + }; + } + + // Keep consistent with LockBinder.TryFindLockTypeInfo. + internal static LockTypeInfo? TryFindLockTypeInfo(this ITypeSymbol lockType) + { + IMethodSymbol? enterLockScopeMethod = TryFindPublicVoidParameterlessMethod(lockType, WellKnownMemberNames.EnterLockScopeMethodName); + if (enterLockScopeMethod is not { ReturnsVoid: false, RefKind: RefKind.None }) + { + return null; + } + + ITypeSymbol? scopeType = enterLockScopeMethod.ReturnType; + if (scopeType is not INamedTypeSymbol { Name: WellKnownMemberNames.LockScopeTypeName, Arity: 0, IsValueType: true, IsRefLikeType: true, DeclaredAccessibility: Accessibility.Public } || + !lockType.Equals(scopeType.ContainingType, SymbolEqualityComparer.ConsiderEverything)) + { + return null; + } + + IMethodSymbol? disposeMethod = TryFindPublicVoidParameterlessMethod(scopeType, WellKnownMemberNames.DisposeMethodName); + if (disposeMethod is not { ReturnsVoid: true }) + { + return null; + } + + return new LockTypeInfo + { + EnterLockScopeMethod = enterLockScopeMethod, + ScopeDisposeMethod = disposeMethod, + }; + } + + // Keep consistent with LockBinder.TryFindPublicVoidParameterlessMethod. + private static IMethodSymbol? TryFindPublicVoidParameterlessMethod(ITypeSymbol type, string name) + { + var members = type.GetMembers(name); + IMethodSymbol? result = null; + foreach (var member in members) + { + if (member is IMethodSymbol + { + Parameters: [], + Arity: 0, + IsStatic: false, + DeclaredAccessibility: Accessibility.Public, + MethodKind: MethodKind.Ordinary, + } method) + { + if (result is not null) + { + // Ambiguous method found. + return null; + } + + result = method; + } + } + + return result; + } } } diff --git a/src/Compilers/Core/Portable/Symbols/WellKnownMemberNames.cs b/src/Compilers/Core/Portable/Symbols/WellKnownMemberNames.cs index 090f5abb1fc50..bf49f2a77cd99 100644 --- a/src/Compilers/Core/Portable/Symbols/WellKnownMemberNames.cs +++ b/src/Compilers/Core/Portable/Symbols/WellKnownMemberNames.cs @@ -398,5 +398,9 @@ public static class WellKnownMemberNames /// The name of a type synthesized for a top-level statements entry point method. /// public const string TopLevelStatementsEntryPointTypeName = "Program"; + + internal const string LockTypeName = "Lock"; + internal const string EnterLockScopeMethodName = "EnterLockScope"; + internal const string LockScopeTypeName = "Scope"; } } diff --git a/src/Compilers/VisualBasic/Portable/Binding/Binder_Statements.vb b/src/Compilers/VisualBasic/Portable/Binding/Binder_Statements.vb index 38d0234067a96..8c239074929b9 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/Binder_Statements.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/Binder_Statements.vb @@ -4732,7 +4732,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic lockExpression.Syntax, ErrorFactory.ErrorInfo(ERRID.ERR_SyncLockRequiresReferenceType1, lockExpressionType)) ElseIf lockExpressionType.IsWellKnownTypeLock() Then - ReportDiagnostic(diagnostics, lockExpression.Syntax, ERRID.WRN_LockTypeUnsupported) + ReportDiagnostic(diagnostics, lockExpression.Syntax, ERRID.ERR_LockTypeUnsupported) End If End If diff --git a/src/Compilers/VisualBasic/Portable/Errors/ErrorFacts.vb b/src/Compilers/VisualBasic/Portable/Errors/ErrorFacts.vb index 66ddea68aea72..8c5a24af80636 100644 --- a/src/Compilers/VisualBasic/Portable/Errors/ErrorFacts.vb +++ b/src/Compilers/VisualBasic/Portable/Errors/ErrorFacts.vb @@ -1538,7 +1538,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ERRID.WRN_AnalyzerReferencesNewerCompiler, ERRID.WRN_DuplicateAnalyzerReference, ERRID.ERR_InvalidExperimentalDiagID, - ERRID.WRN_LockTypeUnsupported, + ERRID.ERR_LockTypeUnsupported, ERRID.WRN_ConvertingLock Return False Case Else diff --git a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb index 2c042bc66798d..0b86a4e99a8d7 100644 --- a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb +++ b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb @@ -1778,7 +1778,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ERR_InvalidExperimentalDiagID = 37328 - ERR_NextAvailable = 37329 + ERR_LockTypeUnsupported = 37329 + + ERR_NextAvailable = 37330 '// WARNINGS BEGIN HERE WRN_UseOfObsoleteSymbol2 = 40000 @@ -2011,8 +2013,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic WRN_AnalyzerReferencesNewerCompiler = 42506 WRN_DuplicateAnalyzerReference = 42507 - WRN_LockTypeUnsupported = 42508 - WRN_ConvertingLock = 42509 + WRN_ConvertingLock = 42508 ' // AVAILABLE 42600 - 49998 WRN_NextAvailable = 42600 diff --git a/src/Compilers/VisualBasic/Portable/Generated/ErrorFacts.Generated.vb b/src/Compilers/VisualBasic/Portable/Generated/ErrorFacts.Generated.vb index 12477abf24573..a325413f92031 100644 --- a/src/Compilers/VisualBasic/Portable/Generated/ErrorFacts.Generated.vb +++ b/src/Compilers/VisualBasic/Portable/Generated/ErrorFacts.Generated.vb @@ -178,7 +178,6 @@ ERRID.WRN_CallerArgumentExpressionAttributeHasInvalidParameterName, ERRID.WRN_AnalyzerReferencesNewerCompiler, ERRID.WRN_DuplicateAnalyzerReference, - ERRID.WRN_LockTypeUnsupported, ERRID.WRN_ConvertingLock Return True Case Else diff --git a/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbolExtensions.vb b/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbolExtensions.vb index f64ab8b7ec4c4..8dbdcb3e68379 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbolExtensions.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbolExtensions.vb @@ -1297,14 +1297,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Return typeSymbol.IsWellKnownCompilerServicesTopLevelType("IsExternalInit") End Function + ' Keep in sync with C# equivalent. Friend Function IsWellKnownTypeLock(typeSymbol As TypeSymbol) As Boolean Dim namedTypeSymbol = TryCast(typeSymbol, NamedTypeSymbol) Return namedTypeSymbol IsNot Nothing AndAlso - namedTypeSymbol.Name = "Lock" AndAlso + namedTypeSymbol.Name = WellKnownMemberNames.LockTypeName AndAlso namedTypeSymbol.Arity = 0 AndAlso namedTypeSymbol.ContainingType Is Nothing AndAlso - namedTypeSymbol.IsContainedInNamespace("System", "Threading") + namedTypeSymbol.IsContainedInNamespace(NameOf(System), NameOf(System.Threading)) End Function diff --git a/src/Compilers/VisualBasic/Portable/VBResources.resx b/src/Compilers/VisualBasic/Portable/VBResources.resx index 2d006482b0d00..f2b931852c9dd 100644 --- a/src/Compilers/VisualBasic/Portable/VBResources.resx +++ b/src/Compilers/VisualBasic/Portable/VBResources.resx @@ -5695,11 +5695,8 @@ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in SyncLock statement. diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.cs.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.cs.xlf index 2ce6ef094477b..b333be48a0d22 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.cs.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.cs.xlf @@ -47,6 +47,11 @@ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier + + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + + Multiple analyzer config files cannot be in the same directory ('{0}'). Ve stejném adresáři nemůže být více konfiguračních souborů analyzátoru ({0}). @@ -658,16 +663,6 @@ Generátor se nepovedlo inicializovat - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - Wrong number of type arguments Chybný počet argumentů typu diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.de.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.de.xlf index 96fec79a3e6fa..1204d2be354de 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.de.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.de.xlf @@ -47,6 +47,11 @@ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier + + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + + Multiple analyzer config files cannot be in the same directory ('{0}'). Dasselbe Verzeichnis ({0}) darf nicht mehrere Konfigurationsdateien des Analysetools enthalten. @@ -658,16 +663,6 @@ Fehler beim Initialisieren des Generators. - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - Wrong number of type arguments Falsche Anzahl von Typargumenten. diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.es.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.es.xlf index 8d1001b37d3b1..54fb53824bba2 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.es.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.es.xlf @@ -47,6 +47,11 @@ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier + + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + + Multiple analyzer config files cannot be in the same directory ('{0}'). No es posible que un mismo directorio ("{0}") contenga varios archivos de configuración del analizador. @@ -658,16 +663,6 @@ Error de inicialización del generador. - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - Wrong number of type arguments Número de argumentos de tipo incorrecto diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.fr.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.fr.xlf index 842a67d1fcd53..28a3a3ee1b4ea 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.fr.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.fr.xlf @@ -47,6 +47,11 @@ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier + + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + + Multiple analyzer config files cannot be in the same directory ('{0}'). Plusieurs fichiers config d'analyseur ne peuvent pas figurer dans le même répertoire ('{0}'). @@ -658,16 +663,6 @@ Échec de l'initialisation du générateur. - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - Wrong number of type arguments Nombre incorrect d'arguments de type diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.it.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.it.xlf index 8ae4327e5565c..7263ac84abc2b 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.it.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.it.xlf @@ -47,6 +47,11 @@ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier + + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + + Multiple analyzer config files cannot be in the same directory ('{0}'). La stessa directory ('{0}') non può contenere più file di configurazione dell'analizzatore. @@ -659,16 +664,6 @@ Non è stato possibile inizializzare il generatore. - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - Wrong number of type arguments Il numero di argomenti di tipo è errato diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ja.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ja.xlf index 66ce095994f20..8b89635200367 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ja.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ja.xlf @@ -47,6 +47,11 @@ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier + + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + + Multiple analyzer config files cannot be in the same directory ('{0}'). 複数のアナライザー構成ファイルを同じディレクトリに入れることはできません ('{0}')。 @@ -660,16 +665,6 @@ ジェネレーターを初期化できませんでした。 - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - Wrong number of type arguments 型引数の数が正しくありません diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ko.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ko.xlf index 53d793d4ce4a3..eb619cd416b1c 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ko.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ko.xlf @@ -47,6 +47,11 @@ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier + + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + + Multiple analyzer config files cannot be in the same directory ('{0}'). 분석기 구성 파일 여러 개가 동일한 디렉터리('{0}')에 있을 수 없습니다. @@ -658,16 +663,6 @@ 생성기가 초기화하지 못했습니다. - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - Wrong number of type arguments 형식 인수 수가 잘못되었습니다. diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.pl.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.pl.xlf index a46f65d8666e6..e120812f49183 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.pl.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.pl.xlf @@ -47,6 +47,11 @@ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier + + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + + Multiple analyzer config files cannot be in the same directory ('{0}'). Wiele plików konfiguracji analizatora nie może znajdować się w tym samym katalogu („{0}”). @@ -658,16 +663,6 @@ Generator nie mógł przeprowadzić inicjalizacji. - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - Wrong number of type arguments Nieprawidłowa liczba argumentów typu diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.pt-BR.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.pt-BR.xlf index a03fab4e5df00..ad048539ebd83 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.pt-BR.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.pt-BR.xlf @@ -47,6 +47,11 @@ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier + + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + + Multiple analyzer config files cannot be in the same directory ('{0}'). Não é possível que haja vários arquivos de configuração do analisador no mesmo diretório ('{0}'). @@ -658,16 +663,6 @@ Falha na inicialização do gerador. - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - Wrong number of type arguments Número errado de argumentos de tipo diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ru.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ru.xlf index 3ac0d4cb02d1c..135be70ebcbab 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ru.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ru.xlf @@ -47,6 +47,11 @@ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier + + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + + Multiple analyzer config files cannot be in the same directory ('{0}'). В одном каталоге ("{0}") не может находиться несколько файлов конфигурации анализатора. @@ -658,16 +663,6 @@ optionstrict[+|-] Принудительное применени Не удалось инициализировать генератор. - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - Wrong number of type arguments Неверное число аргументов типа diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.tr.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.tr.xlf index bd7cd058b8e21..b7666cd167166 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.tr.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.tr.xlf @@ -47,6 +47,11 @@ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier + + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + + Multiple analyzer config files cannot be in the same directory ('{0}'). Birden çok çözümleyici yapılandırma dosyası aynı dizinde ('{0}') olamaz. @@ -659,16 +664,6 @@ Oluşturucu başlatılamadı. - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - Wrong number of type arguments Tür bağımsız değişkenlerinin yanlış sayısı diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hans.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hans.xlf index 8935584172d00..18e1eaa29285d 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hans.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hans.xlf @@ -47,6 +47,11 @@ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier + + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + + Multiple analyzer config files cannot be in the same directory ('{0}'). 多个分析器配置文件不能位于同一目录({0})中。 @@ -658,16 +663,6 @@ 生成器初始化失败。 - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - Wrong number of type arguments 类型参数的数目不正确 diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hant.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hant.xlf index 0d958971ab791..24d63987a4873 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hant.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hant.xlf @@ -47,6 +47,11 @@ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier + + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. + + Multiple analyzer config files cannot be in the same directory ('{0}'). 多個分析器組態檔無法處於相同目錄 ('{0}') 中。 @@ -659,16 +664,6 @@ 產生器無法初始化。 - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - - - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. - - Wrong number of type arguments 類型引數的數目錯誤 diff --git a/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_ILockStatement.vb b/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_ILockStatement.vb index c3451ce1593b6..b739e30a078b8 100644 --- a/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_ILockStatement.vb +++ b/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_ILockStatement.vb @@ -9,6 +9,20 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Semantics Partial Public Class IOperationTests Inherits SemanticModelTestBase + Private Const LockTypeDefinition As String = " +namespace System.Threading +{ + public class Lock + { + public Scope EnterLockScope() => new Scope(); + + public ref struct Scope + { + public void Dispose() { } + } + } +}" + Public Sub ILockStatement_ObjectLock_FieldReference() @@ -362,6 +376,86 @@ ILockOperation (OperationKind.Lock, Type: null) (Syntax: 'SyncLock o' ... nd Syn VerifyOperationTreeAndDiagnosticsForTest(Of SyncLockBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) End Sub + + Public Sub ILockStatement_LockObject() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = .Value + + VerifyOperationTreeAndDiagnosticsForTest(Of SyncLockBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub ILockStatement_LockObjectWithAllMembers() + Dim lockRef = CreateCSharpCompilation(LockTypeDefinition).VerifyDiagnostics().EmitToImageReference() + + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = .Value + + VerifyOperationTreeAndDiagnosticsForTest(Of SyncLockBlockSyntax)(source, expectedOperationTree, expectedDiagnostics, references:={lockRef}) + End Sub + Public Sub LockFlow_04() @@ -469,5 +563,235 @@ Block[B6] - Exit VerifyFlowGraphAndDiagnosticsForTest(Of MethodBlockSyntax)(source, expectedFlowGraph, expectedDiagnostics) End Sub + + Public Sub LockFlow_LockObject() + Dim source = .Value + + Dim expectedDiagnostics = .Value + + Dim expectedFlowGraph = .Value + + VerifyFlowGraphAndDiagnosticsForTest(Of MethodBlockSyntax)(source, expectedFlowGraph, expectedDiagnostics) + End Sub + + + Public Sub LockFlow_LockObject_NonEmptyBody() + Dim source = .Value + + Dim expectedDiagnostics = .Value + + Dim expectedFlowGraph = .Value + + VerifyFlowGraphAndDiagnosticsForTest(Of MethodBlockSyntax)(source, expectedFlowGraph, expectedDiagnostics) + End Sub + + + Public Sub LockFlow_LockObject_ConditionalBody() + Dim source = .Value + + Dim expectedDiagnostics = .Value + + Dim expectedFlowGraph = .Value + + VerifyFlowGraphAndDiagnosticsForTest(Of MethodBlockSyntax)(source, expectedFlowGraph, expectedDiagnostics) + End Sub + + + Public Sub LockFlow_LockObjectWithAllMembers() + Dim lockRef = CreateCSharpCompilation(LockTypeDefinition).VerifyDiagnostics().EmitToImageReference() + + Dim source = .Value + + Dim expectedDiagnostics = .Value + + Dim expectedFlowGraph = .Value + + VerifyFlowGraphAndDiagnosticsForTest(Of MethodBlockSyntax)(source, expectedFlowGraph, expectedDiagnostics, additionalReferences:={lockRef}) + End Sub + End Class End Namespace diff --git a/src/Compilers/VisualBasic/Test/Semantic/Binding/SyncLockTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Binding/SyncLockTests.vb index dccb543382392..f2251ff14ec98 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Binding/SyncLockTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Binding/SyncLockTests.vb @@ -492,7 +492,7 @@ Namespace System.Threading End Namespace " CreateCompilation(source).AssertTheseDiagnostics( -"BC42508: A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. +"BC37329: A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. SyncLock l ~ ") @@ -597,7 +597,7 @@ End Namespace Dim comp = CreateCompilation(source) If declaration = "Lock" Then comp.AssertTheseDiagnostics( -"BC42508: A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. +"BC37329: A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. SyncLock l ~ ") @@ -661,25 +661,25 @@ Namespace System.Threading End Namespace " CreateCompilation(source).AssertTheseDiagnostics( -"BC42509: A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in SyncLock statement. +"BC42508: A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in SyncLock statement. Dim o As Object = l ~ -BC42509: A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in SyncLock statement. +BC42508: A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in SyncLock statement. o = DirectCast(l, Object) ~ -BC42509: A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in SyncLock statement. +BC42508: A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in SyncLock statement. SyncLock DirectCast(l, Object) ~ -BC42509: A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in SyncLock statement. +BC42508: A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in SyncLock statement. o = CType(l, Object) ~ -BC42509: A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in SyncLock statement. +BC42508: A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in SyncLock statement. SyncLock CType(l, Object) ~ -BC42509: A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in SyncLock statement. +BC42508: A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in SyncLock statement. o = TryCast(l, Object) ~ -BC42509: A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in SyncLock statement. +BC42508: A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in SyncLock statement. SyncLock TryCast(l, Object) ~ ") @@ -709,7 +709,7 @@ Namespace System.Threading End Namespace " CreateCompilation(source).AssertTheseDiagnostics( -"BC42509: A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in SyncLock statement. +"BC42508: A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in SyncLock statement. Dim o As LockBase = l ~ ") @@ -739,7 +739,7 @@ Namespace System.Threading End Namespace " CreateCompilation(source).AssertTheseDiagnostics( -"BC42509: A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in SyncLock statement. +"BC42508: A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in SyncLock statement. Dim o As ILockBase = l ~ ") @@ -799,22 +799,22 @@ Namespace System.Threading End Namespace " CreateCompilation(source).AssertTheseDiagnostics( -"BC42508: A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. +"BC37329: A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. SyncLock DirectCast(l, Lock) ~~~~~~~~~~~~~~~~~~~ -BC42508: A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. +BC37329: A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. SyncLock CType(l, Lock) ~~~~~~~~~~~~~~ -BC42508: A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. +BC37329: A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. SyncLock TryCast(l, Lock) ~~~~~~~~~~~~~~~~ -BC42508: A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. +BC37329: A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. SyncLock M1(l) ~~~~~ -BC42508: A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. +BC37329: A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. SyncLock M2(l) ~~~~~ -BC42508: A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. +BC37329: A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. SyncLock M3(l) ~~~~~ ") @@ -839,10 +839,10 @@ Namespace System.Threading End Namespace " CreateCompilation(source).AssertTheseDiagnostics( -"BC42509: A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in SyncLock statement. +"BC42508: A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in SyncLock statement. Dim o As Object = l ~ -BC42508: A value of type 'System.Threading.Lock' in SyncLock will use likely unintended monitor-based locking. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. +BC37329: A value of type 'System.Threading.Lock' is not supported in SyncLock. Consider manually calling 'Enter' and 'Exit' methods in a Try/Finally block instead. SyncLock CType(o, Lock) ~~~~~~~~~~~~~~ ")