| File: Microsoft.NetCore.Analyzers\Runtime\DoNotLockOnObjectsWithWeakIdentity.cs | Web Access |
| Project: src\sdk\src\Microsoft.CodeAnalysis.NetAnalyzers\src\Microsoft.CodeAnalysis.NetAnalyzers\Microsoft.CodeAnalysis.NetAnalyzers.csproj (Microsoft.CodeAnalysis.NetAnalyzers) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Immutable; using Analyzer.Utilities; using Analyzer.Utilities.Extensions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Microsoft.NetCore.Analyzers.Runtime { using static MicrosoftNetCoreAnalyzersResources; /// <summary> /// CA2002: <inheritdoc cref="DoNotLockOnObjectsWithWeakIdentityTitle"/> /// /// Cause: /// A thread that attempts to acquire a lock on an object that has a weak identity could cause hangs. /// /// Description: /// An object is said to have a weak identity when it can be directly accessed across application domain boundaries. /// A thread that tries to acquire a lock on an object that has a weak identity can be blocked by a second thread in /// a different application domain that has a lock on the same object. /// </summary> [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class DoNotLockOnObjectsWithWeakIdentityAnalyzer : DiagnosticAnalyzer { internal const string RuleId = "CA2002"; internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( RuleId, CreateLocalizableResourceString(nameof(DoNotLockOnObjectsWithWeakIdentityTitle)), CreateLocalizableResourceString(nameof(DoNotLockOnObjectsWithWeakIdentityMessage)), DiagnosticCategory.Reliability, RuleLevel.CandidateForRemoval, // .NET core only has one appdomain description: CreateLocalizableResourceString(nameof(DoNotLockOnObjectsWithWeakIdentityDescription)), isPortedFxCopRule: true, isDataflowRule: false); public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.RegisterCompilationStartAction(compilationStartContext => { var compilation = compilationStartContext.Compilation; compilationStartContext.RegisterOperationAction( context => ReportOnWeakIdentityObject(((ILockOperation)context.Operation).LockedValue, context), OperationKind.Lock); compilationStartContext.RegisterOperationAction(context => { var invocationOperation = (IInvocationOperation)context.Operation; var method = invocationOperation.TargetMethod; if ((method.Name != "Enter" && method.Name != "TryEnter") || invocationOperation.Arguments.IsEmpty) { return; } if (compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingMonitor, out var monitorType) && method.ContainingType.Equals(monitorType) && invocationOperation.Arguments[0].Value is IConversionOperation conversionOperation) { ReportOnWeakIdentityObject(conversionOperation.Operand, context); } }, OperationKind.Invocation); }); } private static void ReportOnWeakIdentityObject(IOperation operation, OperationAnalysisContext context) { if (operation is IInstanceReferenceOperation instanceReference && instanceReference.ReferenceKind == InstanceReferenceKind.ContainingTypeInstance) { context.ReportDiagnostic(operation.CreateDiagnostic(Rule, operation.Syntax.ToString())); } else { if (operation.Type is ITypeSymbol type && TypeHasWeakIdentity(type, context.Compilation)) { context.ReportDiagnostic(operation.CreateDiagnostic(Rule, type.ToDisplayString())); } } } private static bool TypeHasWeakIdentity(ITypeSymbol type, Compilation compilation) { switch (type.TypeKind) { case TypeKind.Array: return type is IArrayTypeSymbol arrayType && IsPrimitiveType(arrayType.ElementType); case TypeKind.Class: case TypeKind.TypeParameter: INamedTypeSymbol? marshalByRefObjectTypeSymbol = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemMarshalByRefObject); INamedTypeSymbol? executionEngineExceptionTypeSymbol = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemExecutionEngineException); INamedTypeSymbol? outOfMemoryExceptionTypeSymbol = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemOutOfMemoryException); INamedTypeSymbol? stackOverflowExceptionTypeSymbol = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemStackOverflowException); INamedTypeSymbol? memberInfoTypeSymbol = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemReflectionMemberInfo); INamedTypeSymbol? parameterInfoTypeSymbol = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemReflectionParameterInfo); INamedTypeSymbol? threadTypeSymbol = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingThread); return type.SpecialType == SpecialType.System_String || type.Equals(executionEngineExceptionTypeSymbol) || type.Equals(outOfMemoryExceptionTypeSymbol) || type.Equals(stackOverflowExceptionTypeSymbol) || type.Inherits(marshalByRefObjectTypeSymbol) || type.Inherits(memberInfoTypeSymbol) || type.Inherits(parameterInfoTypeSymbol) || type.Inherits(threadTypeSymbol); // What about struct types? default: return false; } } public static bool IsPrimitiveType(ITypeSymbol type) { return type.SpecialType switch { SpecialType.System_Boolean or SpecialType.System_Byte or SpecialType.System_Char or SpecialType.System_Double or SpecialType.System_Int16 or SpecialType.System_Int32 or SpecialType.System_Int64 or SpecialType.System_UInt16 or SpecialType.System_UInt32 or SpecialType.System_UInt64 or SpecialType.System_IntPtr or SpecialType.System_UIntPtr or SpecialType.System_SByte or SpecialType.System_Single => true, _ => false, }; } } }