├── doc
└── logo.jpg
├── src
├── MultithreadingAnalyzer.Test
│ ├── CopiedSpinLock
│ │ ├── TestData
│ │ │ ├── 002_ReadonlySpinLock.cs
│ │ │ └── 001_SpinLockPassByValue.cs
│ │ ├── CopiedSpinLockAnalyzerTest.cs
│ │ ├── TestCases.Designer.cs
│ │ └── TestCases.resx
│ ├── DeprecatedReaderWriterLock
│ │ ├── TestData
│ │ │ └── 001_ReaderWriterLock.cs
│ │ ├── DeprecatedReaderWriterLockAnalyzerTest.cs
│ │ ├── TestCases.Designer.cs
│ │ └── TestCases.resx
│ ├── MethodLevelSynchronization
│ │ ├── TestData
│ │ │ └── 001_MethodLevelSyncrhonization.cs
│ │ ├── MethodLevelSynchronizationAnalyzerTests.cs
│ │ ├── TestCases.Designer.cs
│ │ └── TestCases.resx
│ ├── LockObjectSelection
│ │ ├── TestData
│ │ │ ├── 005_LockOnThisInstance.cs
│ │ │ ├── 006_LockOnValueType.cs
│ │ │ ├── 003_LockOnPublicField.cs
│ │ │ ├── 001_LockOnNonReadonlyField.cs
│ │ │ ├── 004_LockOnPublicProperty.cs
│ │ │ ├── 002_LockOnNonReadonlyProperty.cs
│ │ │ └── 007_LockOnObjectWithWeakIdentity.cs
│ │ ├── TestCases.resx
│ │ ├── LockObjectSelectionAnalyzerTests.cs
│ │ └── TestCases.Designer.cs
│ ├── AbandonLock
│ │ ├── TestData
│ │ │ ├── 003_LockReleasedOutsideFinallyClause.cs
│ │ │ ├── 002_LockAcquiredInsideTryClause.cs
│ │ │ └── 001_LockAcquiredOutsideTryClause.cs
│ │ ├── TestCases.Designer.cs
│ │ ├── TestCases.resx
│ │ └── AbandonLockAnalyzerTest.cs
│ └── SmartAnalyzers.MultithreadingAnalyzer.Test.csproj
├── MultithreadingAnalyzer
│ ├── MethodDescriptor.cs
│ ├── Utils
│ │ └── ExpressionHelpers.cs
│ ├── SymbolHelper.cs
│ ├── tools
│ │ ├── install.ps1
│ │ └── uninstall.ps1
│ ├── BasicLockAnalyzer.cs
│ ├── SmartAnalyzers.MultithreadingAnalyzer.csproj
│ ├── DeprecatedReaderWriterLockAnalyzer.cs
│ ├── MethodLevelSynchronizationAnalyzer.cs
│ ├── CopiedSpinLockAnalyzer.cs
│ ├── AbandonLockAnalyzer.cs
│ └── LockObjectSelectionAnalyzer.cs
├── MultithreadingAnalyzer.sln.DotSettings
├── MultithreadingAnalyzer.Vsix
│ ├── source.extension.vsixmanifest
│ └── SmartAnalyzers.MultithreadingAnalyzer.Vsix.csproj
└── MultithreadingAnalyzer.sln
├── README.md
├── LICENSE
├── appveyor.yml
└── .gitignore
/doc/logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cezarypiatek/MultithreadingAnalyzer/HEAD/doc/logo.jpg
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/CopiedSpinLock/TestData/002_ReadonlySpinLock.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 |
3 | namespace SmartAnalyzers.MultithreadingAnalyzer.Test.CopiedSpinLock.TestData
4 | {
5 | class _002_ReadonlySpinLock
6 | {
7 | private readonly SpinLock InvalidSpinLock = new SpinLock();
8 | private SpinLock ValidSpinLock = new SpinLock();
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/DeprecatedReaderWriterLock/TestData/001_ReaderWriterLock.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 |
3 | namespace SmartAnalyzers.MultithreadingAnalyzer.Test.CopiedSpinLock.TestData
4 | {
5 | class _003_ReaderWriterLock
6 | {
7 | private ReaderWriterLock ReaderWriterLockProperty { get; set; }
8 |
9 | private ReaderWriterLock ReaderWriterLockField;
10 |
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/MethodLevelSynchronization/TestData/001_MethodLevelSyncrhonization.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 | using System.Threading;
3 |
4 | namespace SmartAnalyzers.MultithreadingAnalyzer.Test.LockObjectSelection.TestData
5 | {
6 | class _001_MethodLevelSynchronization
7 | {
8 |
9 | [MethodImpl(MethodImplOptions.Synchronized)]
10 | public void DoSth3()
11 | {
12 | }
13 |
14 | [MethodImpl( MethodImplOptions.NoInlining | MethodImplOptions.Synchronized)]
15 | public void DoSth4()
16 | {
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer/MethodDescriptor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace SmartAnalyzers.MultithreadingAnalyzer
4 | {
5 | class MethodDescriptor
6 | {
7 | public string FullName { get; }
8 |
9 | public MethodDescriptor(string fullName)
10 | {
11 | FullName = fullName;
12 | var parts = fullName.Split('.');
13 | this.Name = parts[parts.Length-1];
14 | this.TypeFullName = String.Join(".", parts, 0, parts.Length - 1);
15 | }
16 |
17 | public string TypeFullName { get; }
18 |
19 | public string Name { get; }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/LockObjectSelection/TestData/005_LockOnThisInstance.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 | using System.Threading;
3 |
4 | namespace SmartAnalyzers.MultithreadingAnalyzer.Test.LockObjectSelection.TestData
5 | {
6 | class _005_LockOnThisInstance
7 | {
8 | public void DoSth1()
9 | {
10 | Monitor.Enter(this);
11 | Monitor.Exit(this);
12 | }
13 |
14 | public void DoSth2()
15 | {
16 | var wasTaken = false;
17 | Monitor.TryEnter(this, ref wasTaken);
18 | if (wasTaken)
19 | {
20 | Monitor.Exit(this);
21 | }
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.sln.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | True
3 | True
4 | True
5 | True
6 | True
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/LockObjectSelection/TestData/006_LockOnValueType.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 |
3 | namespace SmartAnalyzers.MultithreadingAnalyzer.Test.LockObjectSelection.TestData
4 | {
5 | struct MyStruct
6 | {
7 | public int SampleProp { get; set; }
8 | }
9 |
10 | class _006_LockOnValueType
11 | {
12 | private MyStruct lockObj = new MyStruct();
13 |
14 | public void DoSth1()
15 | {
16 | Monitor.Enter(lockObj);
17 | Monitor.Exit(lockObj);
18 | }
19 |
20 | public void DoSth2()
21 | {
22 | var wasTaken = false;
23 | Monitor.TryEnter(lockObj, ref wasTaken);
24 | if (wasTaken)
25 | {
26 | Monitor.Exit(lockObj);
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/LockObjectSelection/TestData/003_LockOnPublicField.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 |
3 | namespace SmartAnalyzers.MultithreadingAnalyzer.Test.LockObjectSelection.TestData
4 | {
5 | class _003_LockOnPublicField
6 | {
7 | public object lockobj = new object();
8 |
9 | public void DoSth1()
10 | {
11 | lock (lockobj)
12 | {
13 | }
14 | }
15 |
16 | public void DoSth2()
17 | {
18 | Monitor.Enter(lockobj);
19 | Monitor.Exit(lockobj);
20 | }
21 |
22 | public void DoSth3()
23 | {
24 | var wasTaken = false;
25 | Monitor.TryEnter(lockobj, ref wasTaken);
26 | if (wasTaken)
27 | {
28 | Monitor.Exit(lockobj);
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/LockObjectSelection/TestData/001_LockOnNonReadonlyField.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 |
3 | namespace SmartAnalyzers.MultithreadingAnalyzer.Test.LockObjectSelection.TestData
4 | {
5 | class _001_LockOnNonReadonlyField
6 | {
7 | private object lockobj = new object();
8 |
9 | public void DoSth1()
10 | {
11 | lock (lockobj)
12 | {
13 | }
14 | }
15 |
16 | public void DoSth2()
17 | {
18 | Monitor.Enter(lockobj);
19 | Monitor.Exit(lockobj);
20 | }
21 |
22 | public void DoSth3()
23 | {
24 | var wasTaken = false;
25 | Monitor.TryEnter(lockobj, ref wasTaken);
26 | if (wasTaken)
27 | {
28 | Monitor.Exit(lockobj);
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/LockObjectSelection/TestData/004_LockOnPublicProperty.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 |
3 | namespace SmartAnalyzers.MultithreadingAnalyzer.Test.LockObjectSelection.TestData
4 | {
5 | class _004_LockOnPublicProperty
6 | {
7 | public object LockObj { get; set; } = new object();
8 |
9 | public void DoSth1()
10 | {
11 | lock (LockObj)
12 | {
13 | }
14 | }
15 |
16 | public void DoSth2()
17 | {
18 | Monitor.Enter(LockObj);
19 | Monitor.Exit(LockObj);
20 | }
21 |
22 | public void DoSth3()
23 | {
24 | var wasTaken = false;
25 | Monitor.TryEnter(LockObj, ref wasTaken);
26 | if (wasTaken)
27 | {
28 | Monitor.Exit(LockObj);
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/LockObjectSelection/TestData/002_LockOnNonReadonlyProperty.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 |
3 | namespace SmartAnalyzers.MultithreadingAnalyzer.Test.LockObjectSelection.TestData
4 | {
5 | class _002_LockOnNonReadonlyProperty
6 | {
7 | private object LockObj { get; set; } = new object();
8 |
9 | public void DoSth1()
10 | {
11 | lock (LockObj)
12 | {
13 | }
14 | }
15 |
16 | public void DoSth2()
17 | {
18 | Monitor.Enter(LockObj);
19 | Monitor.Exit(LockObj);
20 | }
21 |
22 | public void DoSth3()
23 | {
24 | var wasTaken = false;
25 | Monitor.TryEnter(LockObj, ref wasTaken);
26 | if (wasTaken)
27 | {
28 | Monitor.Exit(LockObj);
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/DeprecatedReaderWriterLock/DeprecatedReaderWriterLockAnalyzerTest.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using Microsoft.CodeAnalysis.Diagnostics;
3 | using NUnit.Framework;
4 | using RoslynTestKit;
5 |
6 | namespace SmartAnalyzers.MultithreadingAnalyzer.Test.DeprecatedReaderWriterLock
7 | {
8 | public class DeprecatedReaderWriterLockAnalyzerTest : AnalyzerTestFixture
9 | {
10 | protected override string LanguageName => LanguageNames.CSharp;
11 | protected override DiagnosticAnalyzer CreateAnalyzer() => new DeprecatedReaderWriterLockAnalyzer();
12 |
13 | [Test]
14 | public void should_report_readerwriterlock_property() =>
15 | HasDiagnosticAtLine(TestCases._001_ReaderWriterLock, nameof(DeprecatedReaderWriterLockAnalyzer.MT1016), 7);
16 |
17 | [Test]
18 | public void should_report_readerwriterlock_field() =>
19 | HasDiagnosticAtLine(TestCases._001_ReaderWriterLock, nameof(DeprecatedReaderWriterLockAnalyzer.MT1016), 9);
20 | }
21 | }
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/CopiedSpinLock/TestData/001_SpinLockPassByValue.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 |
3 | namespace SmartAnalyzers.MultithreadingAnalyzer.Test.CopiedSpinLock.TestData
4 | {
5 | class _001_SpinLockPassByValue
6 | {
7 | public _001_SpinLockPassByValue(SpinLock spinLock)
8 | {
9 |
10 | }
11 |
12 | public void SynchronizeWith(SpinLock spinLock)
13 | {
14 |
15 | }
16 | }
17 |
18 | class SampleSpinlockWrapper1
19 | {
20 | public SampleSpinlockWrapper1(ref SpinLock spinLock)
21 | {
22 |
23 | }
24 |
25 | public void SynchronizeWith(ref SpinLock spinLock)
26 | {
27 |
28 | }
29 | }
30 |
31 | class SampleSpinlockWrapper2
32 | {
33 | public SampleSpinlockWrapper2(out SpinLock spinLock)
34 | {
35 |
36 | }
37 |
38 | public void SynchronizeWith(out SpinLock spinLock)
39 | {
40 |
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MultithreadingAnalyzer
2 | A set of Roslyn analyzers related to multithreading
3 |
4 | All potential issues diagnosed by `MultithreadingAnalyzer` are described in depth in the following articles:
5 |
6 | - [Avoid multithreading traps with Roslyn: Lock object selection](https://cezarypiatek.github.io/post/avoid-multithreading-traps-p1/)
7 | - [Avoid thread synchronization problems with Roslyn: Synchronization primitives traps](https://cezarypiatek.github.io/post/avoid-multithreading-traps-p2/)
8 |
9 |
10 | ## Currently implemented rules:
11 |
12 | - MT1000: Lock on publicly accessible member
13 | - MT1001: Lock on this reference
14 | - MT1002: Lock on object with weak identity
15 | - MT1003: Lock on non-readonly member
16 | - MT1004: Lock on value type instance
17 | - MT1010: Method level synchronization
18 | - MT1012: Acquiring lock without guarantee of releasing
19 | - MT1013: Releasing lock without guarantee of execution
20 | - MT1014: Passed by value SpinLock is useless
21 | - MT1015: Readonly SpinLock is useless
22 | - MT1016: Replace ReaderWriterLock with ReaderWriterLockSlim
23 |
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 SmartAnalyzers
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/MethodLevelSynchronization/MethodLevelSynchronizationAnalyzerTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using Microsoft.CodeAnalysis.Diagnostics;
3 | using NUnit.Framework;
4 | using RoslynTestKit;
5 | using static SmartAnalyzers.MultithreadingAnalyzer.Test.MethodLevelSynchronization.TestCases;
6 |
7 | namespace SmartAnalyzers.MultithreadingAnalyzer.Test.MethodLevelSynchronization
8 | {
9 | public class MethodLevelSynchronizationAnalyzerTests : AnalyzerTestFixture
10 | {
11 | [Test]
12 | public void do_not_use_method_level_synchronization()
13 | {
14 | HasDiagnosticAtLine(_001_MethodLevelSyncrhonization, MethodLevelSynchronizationAnalyzer.DiagnosticId,9 );
15 | }
16 |
17 | [Test]
18 | public void do_not_use_method_level_synchronization_multiple_flags()
19 | {
20 | HasDiagnosticAtLine(_001_MethodLevelSyncrhonization, MethodLevelSynchronizationAnalyzer.DiagnosticId, 14);
21 | }
22 |
23 | protected override string LanguageName => LanguageNames.CSharp;
24 |
25 | protected override DiagnosticAnalyzer CreateAnalyzer()
26 | {
27 | return new MethodLevelSynchronizationAnalyzer();
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | version: 1.1.{build}
2 | image: Visual Studio 2017
3 | configuration: Release
4 | dotnet_csproj:
5 | patch: true
6 | file: '**\*.csproj'
7 | version: '{version}'
8 | package_version: '{version}'
9 | assembly_version: '{version}'
10 | file_version: '{version}'
11 | informational_version: '{version}'
12 | before_build:
13 | - ps: >-
14 | function Set-VsixVersion {
15 | param(
16 | [Parameter(Mandatory=$true)]
17 | [string]$manifestPath,
18 | [Parameter(Mandatory=$true)]
19 | [string]$Version
20 | )
21 | $manifestXml = [xml](Get-Content $manifestPath -Raw)
22 | $manifestXml.PackageManifest.Metadata.Identity.Version = $Version
23 | $manifestXml.save($manifestPath)
24 | }
25 | Set-VsixVersion -Version "$($env:APPVEYOR_BUILD_VERSION)" -manifestPath C:\projects\multithreadinganalyzer\src\MultithreadingAnalyzer.Vsix\source.extension.vsixmanifest
26 | - cmd: git submodule update --init --recursive
27 | - cmd: nuget restore src
28 | - cmd: dotnet restore src
29 | build:
30 | project: src\MultithreadingAnalyzer.sln
31 | verbosity: minimal
32 | artifacts:
33 | - path: src\MultithreadingAnalyzer.Vsix\bin\Release\SmartAnalyzers.MultithreadingAnalyzer.vsix
34 | name: SmartAnalyzers.MultithreadingAnalyzer.vsix
35 | - path: src\MultithreadingAnalyzer\bin\Release\SmartAnalyzers.MultithreadingAnalyzer.*.nupkg
36 | name: SmartAnalyzers.MultithreadingAnalyzer.*.nupkg
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer/Utils/ExpressionHelpers.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.CodeAnalysis;
3 | using Microsoft.CodeAnalysis.CSharp;
4 | using Microsoft.CodeAnalysis.CSharp.Syntax;
5 | using Microsoft.CodeAnalysis.Diagnostics;
6 |
7 | namespace SmartAnalyzers.MultithreadingAnalyzer.Utils
8 | {
9 | internal static class ExpressionHelpers
10 | {
11 | public static bool IsInvocationOf(SyntaxNodeAnalysisContext context, params MethodDescriptor[] candidates)
12 | {
13 | var invocationExpression = (InvocationExpressionSyntax)context.Node;
14 | if (invocationExpression.Expression is MemberAccessExpressionSyntax memberAccess)
15 | {
16 | var text = memberAccess.ToFullString().Trim();
17 | Lazy symbols = new Lazy(() =>
18 | {
19 | var typeInfo = context.SemanticModel.GetTypeInfo(memberAccess.Expression);
20 | return typeInfo.Type;
21 | });
22 | for (int i = 0; i < candidates.Length; i++)
23 | {
24 | if (text == candidates[i].Name || text.EndsWith($".{candidates[i].Name}"))
25 | {
26 | if(symbols.Value?.ToDisplayString() == candidates[i].TypeFullName)
27 | {
28 | return true;
29 | }
30 | }
31 | }
32 | }
33 |
34 | return false;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Vsix/source.extension.vsixmanifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SmartAnalyzers.MultithreadingAnalyzer
6 | This is a sample diagnostic extension for the .NET Compiler Platform ("Roslyn").
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer/SymbolHelper.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace SmartAnalyzers.MultithreadingAnalyzer
4 | {
5 | internal static class SymbolHelper
6 | {
7 | public static bool IsPrimitiveType(ITypeSymbol type)
8 | {
9 | switch (type.SpecialType)
10 | {
11 | case SpecialType.System_Boolean:
12 | case SpecialType.System_Byte:
13 | case SpecialType.System_Char:
14 | case SpecialType.System_Double:
15 | case SpecialType.System_Int16:
16 | case SpecialType.System_Int32:
17 | case SpecialType.System_Int64:
18 | case SpecialType.System_UInt16:
19 | case SpecialType.System_UInt32:
20 | case SpecialType.System_UInt64:
21 | case SpecialType.System_IntPtr:
22 | case SpecialType.System_UIntPtr:
23 | case SpecialType.System_SByte:
24 | case SpecialType.System_Single:
25 | return true;
26 | default:
27 | return false;
28 | }
29 | }
30 |
31 | public static bool CanBeAssignedTo(ITypeSymbol symbolInfoType, string baseType, bool checkInheritance = true)
32 | {
33 | if (symbolInfoType == null)
34 | {
35 | return false;
36 | }
37 |
38 | if (symbolInfoType.ToString() == baseType)
39 | {
40 | return true;
41 | }
42 |
43 | if (checkInheritance == false)
44 | {
45 |
46 | }
47 | return CanBeAssignedTo(symbolInfoType.BaseType, baseType);
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer/tools/install.ps1:
--------------------------------------------------------------------------------
1 | param($installPath, $toolsPath, $package, $project)
2 |
3 | if($project.Object.SupportsPackageDependencyResolution)
4 | {
5 | if($project.Object.SupportsPackageDependencyResolution())
6 | {
7 | # Do not install analyzers via install.ps1, instead let the project system handle it.
8 | return
9 | }
10 | }
11 |
12 | $analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve
13 |
14 | foreach($analyzersPath in $analyzersPaths)
15 | {
16 | if (Test-Path $analyzersPath)
17 | {
18 | # Install the language agnostic analyzers.
19 | foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll)
20 | {
21 | if($project.Object.AnalyzerReferences)
22 | {
23 | $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName)
24 | }
25 | }
26 | }
27 | }
28 |
29 | # $project.Type gives the language name like (C# or VB.NET)
30 | $languageFolder = ""
31 | if($project.Type -eq "C#")
32 | {
33 | $languageFolder = "cs"
34 | }
35 | if($project.Type -eq "VB.NET")
36 | {
37 | $languageFolder = "vb"
38 | }
39 | if($languageFolder -eq "")
40 | {
41 | return
42 | }
43 |
44 | foreach($analyzersPath in $analyzersPaths)
45 | {
46 | # Install language specific analyzers.
47 | $languageAnalyzersPath = join-path $analyzersPath $languageFolder
48 | if (Test-Path $languageAnalyzersPath)
49 | {
50 | foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll)
51 | {
52 | if($project.Object.AnalyzerReferences)
53 | {
54 | $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName)
55 | }
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer/tools/uninstall.ps1:
--------------------------------------------------------------------------------
1 | param($installPath, $toolsPath, $package, $project)
2 |
3 | if($project.Object.SupportsPackageDependencyResolution)
4 | {
5 | if($project.Object.SupportsPackageDependencyResolution())
6 | {
7 | # Do not uninstall analyzers via uninstall.ps1, instead let the project system handle it.
8 | return
9 | }
10 | }
11 |
12 | $analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve
13 |
14 | foreach($analyzersPath in $analyzersPaths)
15 | {
16 | # Uninstall the language agnostic analyzers.
17 | if (Test-Path $analyzersPath)
18 | {
19 | foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll)
20 | {
21 | if($project.Object.AnalyzerReferences)
22 | {
23 | $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName)
24 | }
25 | }
26 | }
27 | }
28 |
29 | # $project.Type gives the language name like (C# or VB.NET)
30 | $languageFolder = ""
31 | if($project.Type -eq "C#")
32 | {
33 | $languageFolder = "cs"
34 | }
35 | if($project.Type -eq "VB.NET")
36 | {
37 | $languageFolder = "vb"
38 | }
39 | if($languageFolder -eq "")
40 | {
41 | return
42 | }
43 |
44 | foreach($analyzersPath in $analyzersPaths)
45 | {
46 | # Uninstall language specific analyzers.
47 | $languageAnalyzersPath = join-path $analyzersPath $languageFolder
48 | if (Test-Path $languageAnalyzersPath)
49 | {
50 | foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll)
51 | {
52 | if($project.Object.AnalyzerReferences)
53 | {
54 | try
55 | {
56 | $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName)
57 | }
58 | catch
59 | {
60 |
61 | }
62 | }
63 | }
64 | }
65 | }
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer/BasicLockAnalyzer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.CodeAnalysis.CSharp;
3 | using Microsoft.CodeAnalysis.CSharp.Syntax;
4 | using Microsoft.CodeAnalysis.Diagnostics;
5 | using SmartAnalyzers.MultithreadingAnalyzer.Utils;
6 |
7 | namespace SmartAnalyzers.MultithreadingAnalyzer
8 | {
9 | public abstract class BasicLockAnalyzer : DiagnosticAnalyzer
10 | {
11 | public override void Initialize(AnalysisContext context)
12 | {
13 | if (context == null)
14 | throw new ArgumentNullException(nameof(context));
15 |
16 | context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
17 | context.EnableConcurrentExecution();
18 | context.RegisterSyntaxNodeAction(AnalyzeLockStatement, SyntaxKind.LockStatement);
19 | context.RegisterSyntaxNodeAction(AnalyzeMonitorEnterInvocation, SyntaxKind.InvocationExpression);
20 | }
21 |
22 | private void AnalyzeLockStatement(SyntaxNodeAnalysisContext context)
23 | {
24 | var lockStatement = (LockStatementSyntax)context.Node;
25 |
26 | TryToReportViolation(context, lockStatement?.Expression);
27 | }
28 |
29 | private static readonly MethodDescriptor[] LockAcquireMethods =
30 | {
31 | new MethodDescriptor("System.Threading.Monitor.Enter"),
32 | new MethodDescriptor("System.Threading.Monitor.TryEnter")
33 | };
34 |
35 | private void AnalyzeMonitorEnterInvocation(SyntaxNodeAnalysisContext context)
36 | {
37 | var invocationExpression = (InvocationExpressionSyntax)context.Node;
38 | if (ExpressionHelpers.IsInvocationOf(context, LockAcquireMethods))
39 | {
40 | var lockParameterExpression = invocationExpression.ArgumentList?.Arguments.FirstOrDefault()?.Expression;
41 | TryToReportViolation(context, lockParameterExpression);
42 | }
43 | }
44 |
45 | protected abstract void TryToReportViolation(SyntaxNodeAnalysisContext context, ExpressionSyntax expression);
46 | }
47 | }
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/AbandonLock/TestData/003_LockReleasedOutsideFinallyClause.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 |
3 | namespace SmartAnalyzers.MultithreadingAnalyzer.Test.AbandonLock.TestData
4 | {
5 | class _003_LockReleasedOutsideFinallyClause
6 | {
7 | private readonly object myLockObj = new object();
8 |
9 | public void DoSth1()
10 | {
11 | Monitor.Enter(myLockObj);
12 | Monitor.Exit(myLockObj);
13 | }
14 |
15 | private SpinLock mySpinLock = new SpinLock();
16 |
17 | public void DoSth4()
18 | {
19 | var lockAcquired = false;
20 | mySpinLock.TryEnter(ref lockAcquired);
21 | if (lockAcquired)
22 | mySpinLock.Exit();
23 | }
24 |
25 | private readonly Mutex myMutex = new Mutex();
26 |
27 | public void DoSth5()
28 | {
29 | myMutex.WaitOne();
30 | myMutex.ReleaseMutex();
31 | }
32 |
33 | private readonly ReaderWriterLockSlim myReaderWriterLockSlim = new ReaderWriterLockSlim();
34 |
35 | public void DoSth6()
36 | {
37 | myReaderWriterLockSlim.EnterReadLock();
38 | myReaderWriterLockSlim.ExitReadLock();
39 | }
40 |
41 | public void DoSth7()
42 | {
43 | myReaderWriterLockSlim.EnterWriteLock();
44 | myReaderWriterLockSlim.ExitWriteLock();
45 | }
46 |
47 | public void DoSth8()
48 | {
49 | myReaderWriterLockSlim.EnterUpgradeableReadLock();
50 | myReaderWriterLockSlim.ExitUpgradeableReadLock();
51 | }
52 |
53 | private readonly System.Threading.ReaderWriterLock myReaderWriterLock = new System.Threading.ReaderWriterLock();
54 |
55 | public void DoSth9()
56 | {
57 | myReaderWriterLock.AcquireReaderLock(1000);
58 | myReaderWriterLock.ReleaseReaderLock();
59 | }
60 |
61 | public void DoSth10()
62 | {
63 | myReaderWriterLock.AcquireWriterLock(1000);
64 | myReaderWriterLock.ReleaseWriterLock();
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer/SmartAnalyzers.MultithreadingAnalyzer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard1.3
5 | false
6 | True
7 |
8 |
9 |
10 | SmartAnalyzers.MultithreadingAnalyzer
11 | 1.0.0.0
12 | Cezary Piatek
13 | https://github.com/smartanalyzers/MultithreadingAnalyzer/blob/master/LICENSE
14 | https://github.com/smartanalyzers/MultithreadingAnalyzer
15 | https://github.com/smartanalyzers/MultithreadingAnalyzer/raw/master/doc/logo.jpg
16 | https://github.com/smartanalyzers/MultithreadingAnalyzer
17 | false
18 | A set of Roslyn analyzers related to multithreading
19 |
20 | Copyright
21 | SmartAnalyzers.MultithreadingAnalyzer, analyzers, multithreading
22 | true
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/CopiedSpinLock/CopiedSpinLockAnalyzerTest.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using Microsoft.CodeAnalysis.Diagnostics;
3 | using NUnit.Framework;
4 | using RoslynTestKit;
5 |
6 | namespace SmartAnalyzers.MultithreadingAnalyzer.Test.CopiedSpinLock
7 | {
8 | public class CopiedSpinLockAnalyzerTest: AnalyzerTestFixture
9 | {
10 | [Test]
11 | public void should_report_spinlock_pas_by_value_to_constructor()
12 | => HasDiagnosticAtLine(TestCases._001_SpinLockPassByValue, nameof(CopiedSpinLockAnalyzer.MT1014), 7);
13 |
14 | [Test]
15 | public void should_report_spinlock_pas_by_value_to_method()
16 | => HasDiagnosticAtLine(TestCases._001_SpinLockPassByValue, nameof(CopiedSpinLockAnalyzer.MT1014), 12);
17 |
18 |
19 | [Test]
20 | public void should_not_report_spinlock_pas_by_ref_to_constructor()
21 | => NoDiagnosticAtLine(TestCases._001_SpinLockPassByValue, nameof(CopiedSpinLockAnalyzer.MT1014), 20);
22 |
23 | [Test]
24 | public void should_not_report_spinlock_pas_by_ref_to_method()
25 | => NoDiagnosticAtLine(TestCases._001_SpinLockPassByValue, nameof(CopiedSpinLockAnalyzer.MT1014), 25);
26 |
27 |
28 | [Test]
29 | public void should_not_report_spinlock_pas_by_out_to_constructor()
30 | => NoDiagnosticAtLine(TestCases._001_SpinLockPassByValue, nameof(CopiedSpinLockAnalyzer.MT1014), 33);
31 |
32 | [Test]
33 | public void should_not_report_spinlock_pas_by_out_to_method()
34 | => NoDiagnosticAtLine(TestCases._001_SpinLockPassByValue, nameof(CopiedSpinLockAnalyzer.MT1014), 38);
35 |
36 |
37 | [Test]
38 | public void should_report_readonly_spinlock_pas_by_out_to_method()
39 | => HasDiagnosticAtLine(TestCases._002_ReadonlySpinLock, nameof(CopiedSpinLockAnalyzer.MT1015), 7);
40 |
41 | [Test]
42 | public void should_not_report_non_readonly_spinlock_pas_by_out_to_method()
43 | => NoDiagnosticAtLine(TestCases._002_ReadonlySpinLock, nameof(CopiedSpinLockAnalyzer.MT1015), 8);
44 |
45 |
46 | protected override string LanguageName => LanguageNames.CSharp;
47 | protected override DiagnosticAnalyzer CreateAnalyzer() => new CopiedSpinLockAnalyzer();
48 | }
49 | }
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29123.88
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SmartAnalyzers.MultithreadingAnalyzer", "MultithreadingAnalyzer\SmartAnalyzers.MultithreadingAnalyzer.csproj", "{7FE64323-69FA-4DFC-939F-D53656D6499D}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SmartAnalyzers.MultithreadingAnalyzer.Test", "MultithreadingAnalyzer.Test\SmartAnalyzers.MultithreadingAnalyzer.Test.csproj", "{06CF8843-8339-4A8D-9E7C-232DA9FCD2AE}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmartAnalyzers.MultithreadingAnalyzer.Vsix", "MultithreadingAnalyzer.Vsix\SmartAnalyzers.MultithreadingAnalyzer.Vsix.csproj", "{BD8FF74D-66D5-4D45-B11B-ACF7749094B2}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Release|Any CPU = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {7FE64323-69FA-4DFC-939F-D53656D6499D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {7FE64323-69FA-4DFC-939F-D53656D6499D}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {7FE64323-69FA-4DFC-939F-D53656D6499D}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {7FE64323-69FA-4DFC-939F-D53656D6499D}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {06CF8843-8339-4A8D-9E7C-232DA9FCD2AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {06CF8843-8339-4A8D-9E7C-232DA9FCD2AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {06CF8843-8339-4A8D-9E7C-232DA9FCD2AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {06CF8843-8339-4A8D-9E7C-232DA9FCD2AE}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {BD8FF74D-66D5-4D45-B11B-ACF7749094B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {BD8FF74D-66D5-4D45-B11B-ACF7749094B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {BD8FF74D-66D5-4D45-B11B-ACF7749094B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {BD8FF74D-66D5-4D45-B11B-ACF7749094B2}.Release|Any CPU.Build.0 = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(SolutionProperties) = preSolution
32 | HideSolutionNode = FALSE
33 | EndGlobalSection
34 | GlobalSection(ExtensibilityGlobals) = postSolution
35 | SolutionGuid = {57CC87AE-6356-4AC4-B997-6544B575EDC0}
36 | EndGlobalSection
37 | EndGlobal
38 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer/DeprecatedReaderWriterLockAnalyzer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Immutable;
3 | using Microsoft.CodeAnalysis;
4 | using Microsoft.CodeAnalysis.CSharp;
5 | using Microsoft.CodeAnalysis.CSharp.Syntax;
6 | using Microsoft.CodeAnalysis.Diagnostics;
7 |
8 | namespace SmartAnalyzers.MultithreadingAnalyzer
9 | {
10 | [DiagnosticAnalyzer(LanguageNames.CSharp)]
11 | public class DeprecatedReaderWriterLockAnalyzer : DiagnosticAnalyzer
12 | {
13 | internal const string Category = "Locking";
14 |
15 | public static DiagnosticDescriptor MT1016 = new DiagnosticDescriptor(nameof(MT1016), "Replace ReaderWriterLock with ReaderWriterLockSlim", "Consider using ReaderWriterLockSlim instead of ReaderWriterLock in order to avoid potential deadlocks", Category, DiagnosticSeverity.Warning, true);
16 |
17 | public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(MT1016);
18 |
19 | public override void Initialize(AnalysisContext context)
20 | {
21 | if (context == null)
22 | throw new ArgumentNullException(nameof(context));
23 |
24 | context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
25 | context.EnableConcurrentExecution();
26 |
27 | context.RegisterSyntaxNodeAction(AnalyzeField, SyntaxKind.FieldDeclaration);
28 | context.RegisterSyntaxNodeAction(AnalyzeProperty, SyntaxKind.PropertyDeclaration);
29 | }
30 |
31 | private void AnalyzeProperty(SyntaxNodeAnalysisContext context)
32 | {
33 | var fieldDeclaration = (PropertyDeclarationSyntax)context.Node;
34 | TryToReportViolation(context, fieldDeclaration, fieldDeclaration.Type);
35 | }
36 |
37 | private void AnalyzeField(SyntaxNodeAnalysisContext context)
38 | {
39 | var fieldDeclaration = (FieldDeclarationSyntax)context.Node;
40 | TryToReportViolation(context, fieldDeclaration, fieldDeclaration.Declaration.Type);
41 | }
42 |
43 | private static void TryToReportViolation(SyntaxNodeAnalysisContext context, SyntaxNode memberDeclaration,
44 | TypeSyntax fieldDeclarationType)
45 | {
46 | if (fieldDeclarationType.ToFullString().Trim().EndsWith("ReaderWriterLock"))
47 | {
48 | context.ReportDiagnostic(Diagnostic.Create(MT1016, memberDeclaration.GetLocation()));
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer/MethodLevelSynchronizationAnalyzer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Immutable;
3 | using Microsoft.CodeAnalysis;
4 | using Microsoft.CodeAnalysis.CSharp;
5 | using Microsoft.CodeAnalysis.CSharp.Syntax;
6 | using Microsoft.CodeAnalysis.Diagnostics;
7 |
8 | namespace SmartAnalyzers.MultithreadingAnalyzer
9 | {
10 | [DiagnosticAnalyzer(LanguageNames.CSharp)]
11 | public class MethodLevelSynchronizationAnalyzer : DiagnosticAnalyzer
12 | {
13 | public const string DiagnosticId = "MT1010";
14 | internal static readonly LocalizableString Title = "Method level synchronization";
15 | internal static readonly LocalizableString MessageFormat = "Method level synchronization acquires lock on the whole instance or type and may cause a deadlock.";
16 | internal const string Category = "Locking";
17 |
18 | internal static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Error, true);
19 |
20 | public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule);
21 |
22 | public override void Initialize(AnalysisContext context)
23 | {
24 | if (context == null)
25 | throw new ArgumentNullException(nameof(context));
26 |
27 | context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
28 | context.EnableConcurrentExecution();
29 |
30 | context.RegisterSyntaxNodeAction(AnalyzeMethodAttributes, SyntaxKind.Attribute);
31 | }
32 |
33 | private void AnalyzeMethodAttributes(SyntaxNodeAnalysisContext context)
34 | {
35 | var attributeStatement = (AttributeSyntax)context.Node;
36 |
37 | if (attributeStatement == null || attributeStatement.Name.TryGetInferredMemberName() != "MethodImpl")
38 | {
39 | return;
40 | }
41 |
42 | var argumentExpression = attributeStatement.ArgumentList?.Arguments.FirstOrDefault().Expression;
43 | TryReportViolation(context, argumentExpression);
44 | }
45 |
46 | private void TryReportViolation(SyntaxNodeAnalysisContext context, ExpressionSyntax firstArgumentExpression)
47 | {
48 | if (firstArgumentExpression is MemberAccessExpressionSyntax argumentValue && argumentValue.ToFullString().Trim() == "MethodImplOptions.Synchronized")
49 | {
50 | context.ReportDiagnostic(Diagnostic.Create(Rule, argumentValue.GetLocation()));
51 |
52 | }
53 | else if(firstArgumentExpression is BinaryExpressionSyntax binaryExpression)
54 | {
55 | TryReportViolation(context, binaryExpression.Left);
56 | TryReportViolation(context, binaryExpression.Right);
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Vsix/SmartAnalyzers.MultithreadingAnalyzer.Vsix.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 14.0
6 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
7 |
8 |
9 |
10 | Debug
11 | AnyCPU
12 | AnyCPU
13 | 2.0
14 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
15 | {BD8FF74D-66D5-4D45-B11B-ACF7749094B2}
16 | Library
17 | Properties
18 | SmartAnalyzers.MultithreadingAnalyzer.Vsix
19 | SmartAnalyzers.MultithreadingAnalyzer
20 | v4.6.1
21 | false
22 | false
23 | false
24 | false
25 | false
26 | false
27 | Roslyn
28 |
29 |
30 | true
31 | full
32 | false
33 | bin\Debug\
34 | DEBUG;TRACE
35 | prompt
36 | 4
37 |
38 |
39 | pdbonly
40 | true
41 | bin\Release\
42 | TRACE
43 | prompt
44 | 4
45 |
46 |
47 | Program
48 | $(DevEnvDir)devenv.exe
49 | /rootsuffix Roslyn
50 |
51 |
52 |
53 | Designer
54 |
55 |
56 |
57 |
58 | {7FE64323-69FA-4DFC-939F-D53656D6499D}
59 | SmartAnalyzers.MultithreadingAnalyzer
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer/CopiedSpinLockAnalyzer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Immutable;
3 | using System.Linq;
4 | using Microsoft.CodeAnalysis;
5 | using Microsoft.CodeAnalysis.CSharp;
6 | using Microsoft.CodeAnalysis.CSharp.Syntax;
7 | using Microsoft.CodeAnalysis.Diagnostics;
8 |
9 | namespace SmartAnalyzers.MultithreadingAnalyzer
10 | {
11 | [DiagnosticAnalyzer(LanguageNames.CSharp)]
12 | public class CopiedSpinLockAnalyzer : DiagnosticAnalyzer
13 | {
14 | internal const string Category = "Locking";
15 |
16 | public static DiagnosticDescriptor MT1014 = new DiagnosticDescriptor(nameof(MT1014), "Passed by value SpinLock is useless", (LocalizableString) "SpinLock is a struct and passing it by value results with copy which makes SpinLock useless.", Category, DiagnosticSeverity.Error, true);
17 | public static DiagnosticDescriptor MT1015 = new DiagnosticDescriptor(nameof(MT1015), "Readonly SpinLock is useless", "SpinLock is a struct so `readonly` cause boxing which makes SpinLock useless.", Category, DiagnosticSeverity.Error, true);
18 |
19 | public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(MT1014, MT1015);
20 |
21 | public override void Initialize(AnalysisContext context)
22 | {
23 | if (context == null)
24 | throw new ArgumentNullException(nameof(context));
25 |
26 | context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
27 | context.EnableConcurrentExecution();
28 |
29 | context.RegisterSyntaxNodeAction(AnalyzeParameter, SyntaxKind.Parameter);
30 | context.RegisterSyntaxNodeAction(AnalyzeField, SyntaxKind.FieldDeclaration);
31 | context.RegisterSyntaxNodeAction(AnalyzeProperty, SyntaxKind.PropertyDeclaration);
32 | }
33 |
34 | private void AnalyzeParameter(SyntaxNodeAnalysisContext context)
35 | {
36 | var parameter = (ParameterSyntax)context.Node;
37 |
38 | if (parameter.Type == null)
39 | {
40 | return;
41 | }
42 |
43 | if (parameter.Type.ToFullString().Trim().EndsWith("SpinLock") && parameter.Modifiers.Any(IsRefOrOutModifier) == false)
44 | {
45 | context.ReportDiagnostic(Diagnostic.Create(MT1014, parameter.GetLocation()));
46 | }
47 | }
48 |
49 | private static bool IsRefOrOutModifier(SyntaxToken x)
50 | {
51 | var kind = x.Kind();
52 | return kind == SyntaxKind.RefKeyword || kind == SyntaxKind.OutKeyword;
53 | }
54 |
55 | private void AnalyzeProperty(SyntaxNodeAnalysisContext context)
56 | {
57 | var fieldDeclaration = (PropertyDeclarationSyntax)context.Node;
58 | TryToReportSpinlockPassAsValue(context, fieldDeclaration.Modifiers, fieldDeclaration, fieldDeclaration.Type);
59 | }
60 |
61 | private void AnalyzeField(SyntaxNodeAnalysisContext context)
62 | {
63 | var fieldDeclaration = (FieldDeclarationSyntax)context.Node;
64 | TryToReportSpinlockPassAsValue(context, fieldDeclaration.Modifiers, fieldDeclaration, fieldDeclaration.Declaration.Type);
65 | }
66 |
67 | private static void TryToReportSpinlockPassAsValue(SyntaxNodeAnalysisContext context, SyntaxTokenList modifiers, SyntaxNode memberDeclaration, TypeSyntax fieldDeclarationType)
68 | {
69 | if (fieldDeclarationType.ToFullString().Trim().EndsWith("SpinLock") && modifiers.Any(x => x.Kind() == SyntaxKind.ReadOnlyKeyword))
70 | {
71 | context.ReportDiagnostic(Diagnostic.Create(MT1015, memberDeclaration.GetLocation()));
72 | }
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/DeprecatedReaderWriterLock/TestCases.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace SmartAnalyzers.MultithreadingAnalyzer.Test.DeprecatedReaderWriterLock {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class TestCases {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal TestCases() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SmartAnalyzers.MultithreadingAnalyzer.Test.DeprecatedReaderWriterLock.TestCases", typeof(TestCases).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized string similar to using System.Threading;
65 | ///
66 | ///namespace SmartAnalyzers.MultithreadingAnalyzer.Test.CopiedSpinLock.TestData
67 | ///{
68 | /// class _003_ReaderWriterLock
69 | /// {
70 | /// private ReaderWriterLock ReaderWriterLockProperty { get; set; }
71 | ///
72 | /// private ReaderWriterLock ReaderWriterLockField;
73 | ///
74 | /// }
75 | ///}
76 | ///.
77 | ///
78 | internal static string _001_ReaderWriterLock {
79 | get {
80 | return ResourceManager.GetString("_001_ReaderWriterLock", resourceCulture);
81 | }
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/AbandonLock/TestData/002_LockAcquiredInsideTryClause.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 |
3 | namespace SmartAnalyzers.MultithreadingAnalyzer.Test.AbandonLock.TestData
4 | {
5 | class _002_LockAcquiredInsideTryClause
6 | {
7 | private readonly object myLockObj = new object();
8 |
9 | public void DoSth1()
10 | {
11 | try
12 | {
13 | Monitor.Enter(myLockObj);
14 | }
15 | finally
16 | {
17 | Monitor.Exit(myLockObj);
18 | }
19 | }
20 |
21 | public void DoSth2()
22 | {
23 | var lockAcquired = false;
24 | try
25 | {
26 | lockAcquired = Monitor.TryEnter(myLockObj);
27 | }
28 | finally
29 | {
30 | if(lockAcquired)
31 | Monitor.Exit(myLockObj);
32 | }
33 | }
34 |
35 | private SpinLock mySpinLock = new SpinLock();
36 |
37 | public void DoSth3()
38 | {
39 | var lockAcquired = false;
40 | try
41 | {
42 | mySpinLock.Enter(ref lockAcquired);
43 | }
44 | finally
45 | {
46 | if(lockAcquired)
47 | mySpinLock.Exit();
48 | }
49 | }
50 |
51 | public void DoSth4()
52 | {
53 | var lockAcquired = false;
54 | try
55 | {
56 | mySpinLock.TryEnter(ref lockAcquired);
57 | }
58 | finally
59 | {
60 | if(lockAcquired)
61 | mySpinLock.Exit();
62 | }
63 | }
64 |
65 | private readonly Mutex myMutex = new Mutex();
66 |
67 | public void DoSth5()
68 | {
69 | try
70 | {
71 | myMutex.WaitOne();
72 | }
73 | finally
74 | {
75 | myMutex.ReleaseMutex();
76 | }
77 | }
78 |
79 | private readonly ReaderWriterLockSlim myReaderWriterLockSlim = new ReaderWriterLockSlim();
80 |
81 | public void DoSth6()
82 | {
83 | try
84 | {
85 | myReaderWriterLockSlim.EnterReadLock();
86 | }
87 | finally
88 | {
89 | myReaderWriterLockSlim.ExitReadLock();
90 | }
91 | }
92 |
93 | public void DoSth7()
94 | {
95 | try
96 | {
97 | myReaderWriterLockSlim.EnterWriteLock();
98 | }
99 | finally
100 | {
101 | myReaderWriterLockSlim.ExitWriteLock();
102 | }
103 | }
104 |
105 | public void DoSth8()
106 | {
107 | try
108 | {
109 | myReaderWriterLockSlim.EnterUpgradeableReadLock();
110 | }
111 | finally
112 | {
113 | myReaderWriterLockSlim.ExitUpgradeableReadLock();
114 | }
115 | }
116 |
117 | private readonly System.Threading.ReaderWriterLock myReaderWriterLock = new System.Threading.ReaderWriterLock();
118 |
119 | public void DoSth9()
120 | {
121 | try
122 | {
123 | myReaderWriterLock.AcquireReaderLock(1000);
124 | }
125 | finally
126 | {
127 | myReaderWriterLock.ReleaseReaderLock();
128 | }
129 | }
130 |
131 | public void DoSth10()
132 | {
133 | try
134 | {
135 | myReaderWriterLock.AcquireWriterLock(1000);
136 | }
137 | finally
138 | {
139 | myReaderWriterLock.ReleaseWriterLock();
140 | }
141 | }
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/AbandonLock/TestData/001_LockAcquiredOutsideTryClause.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 |
3 | namespace SmartAnalyzers.MultithreadingAnalyzer.Test.AbandonLock.TestData
4 | {
5 | class _001_LockAcquiredOutsideTryClause
6 | {
7 | private readonly object myLockObj = new object();
8 |
9 | public void DoSth1()
10 | {
11 | Monitor.Enter(myLockObj);
12 | try
13 | {
14 |
15 | }
16 | finally
17 | {
18 | Monitor.Exit(myLockObj);
19 | }
20 | }
21 |
22 | public void DoSth2()
23 | {
24 | var lockAcquired = Monitor.TryEnter(myLockObj);
25 | try
26 | {
27 |
28 | }
29 | finally
30 | {
31 | if(lockAcquired)
32 | Monitor.Exit(myLockObj);
33 | }
34 | }
35 |
36 | private SpinLock mySpinLock = new SpinLock();
37 |
38 | public void DoSth3()
39 | {
40 | var lockAcquired = false;
41 | mySpinLock.Enter(ref lockAcquired);
42 | try
43 | {
44 |
45 | }
46 | finally
47 | {
48 | if(lockAcquired)
49 | Monitor.Exit(myLockObj);
50 | }
51 | }
52 |
53 | public void DoSth4()
54 | {
55 | var lockAcquired = false;
56 | mySpinLock.TryEnter(ref lockAcquired);
57 | try
58 | {
59 |
60 | }
61 | finally
62 | {
63 | if(lockAcquired)
64 | Monitor.Exit(myLockObj);
65 | }
66 | }
67 |
68 | private readonly Mutex myMutex = new Mutex();
69 |
70 | public void DoSth5()
71 | {
72 | myMutex.WaitOne();
73 | try
74 | {
75 |
76 | }
77 | finally
78 | {
79 | myMutex.ReleaseMutex();
80 | }
81 | }
82 |
83 | private readonly ReaderWriterLockSlim myReaderWriterLockSlim = new ReaderWriterLockSlim();
84 |
85 | public void DoSth6()
86 | {
87 | myReaderWriterLockSlim.EnterReadLock();
88 | try
89 | {
90 |
91 | }
92 | finally
93 | {
94 | myReaderWriterLockSlim.ExitReadLock();
95 | }
96 | }
97 |
98 | public void DoSth7()
99 | {
100 | myReaderWriterLockSlim.EnterWriteLock();
101 | try
102 | {
103 |
104 | }
105 | finally
106 | {
107 | myReaderWriterLockSlim.ExitWriteLock();
108 | }
109 | }
110 |
111 | public void DoSth8()
112 | {
113 | myReaderWriterLockSlim.EnterUpgradeableReadLock();
114 | try
115 | {
116 |
117 | }
118 | finally
119 | {
120 | myReaderWriterLockSlim.ExitUpgradeableReadLock();
121 | }
122 | }
123 |
124 | private readonly System.Threading.ReaderWriterLock myReaderWriterLock = new System.Threading.ReaderWriterLock();
125 |
126 | public void DoSth9()
127 | {
128 | myReaderWriterLock.AcquireReaderLock(1000);
129 | try
130 | {
131 |
132 | }
133 | finally
134 | {
135 | myReaderWriterLock.ReleaseReaderLock();
136 | }
137 | }
138 |
139 | public void DoSth10()
140 | {
141 | myReaderWriterLock.AcquireWriterLock(1000);
142 | try
143 | {
144 |
145 | }
146 | finally
147 | {
148 | myReaderWriterLock.ReleaseWriterLock();
149 | }
150 | }
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/MethodLevelSynchronization/TestCases.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace SmartAnalyzers.MultithreadingAnalyzer.Test.MethodLevelSynchronization {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class TestCases {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal TestCases() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SmartAnalyzers.MultithreadingAnalyzer.Test.MethodLevelSynchronization.TestCases", typeof(TestCases).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized string similar to using System.Runtime.CompilerServices;
65 | ///using System.Threading;
66 | ///
67 | ///namespace SmartAnalyzers.MultithreadingAnalyzer.Test.LockObjectSelection.TestData
68 | ///{
69 | /// class _001_MethodLevelSynchronization
70 | /// {
71 | ///
72 | /// [MethodImpl(MethodImplOptions.Synchronized)]
73 | /// public void DoSth3()
74 | /// {
75 | /// }
76 | ///
77 | /// [MethodImpl( MethodImplOptions.NoInlining | MethodImplOptions.Synchronized)]
78 | /// public void DoSth4()
79 | /// {
80 | /// }
81 | /// }
82 | ///}
83 | ///.
84 | ///
85 | internal static string _001_MethodLevelSyncrhonization {
86 | get {
87 | return ResourceManager.GetString("_001_MethodLevelSyncrhonization", resourceCulture);
88 | }
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/CopiedSpinLock/TestCases.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace SmartAnalyzers.MultithreadingAnalyzer.Test.CopiedSpinLock {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class TestCases {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal TestCases() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SmartAnalyzers.MultithreadingAnalyzer.Test.CopiedSpinLock.TestCases", typeof(TestCases).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized string similar to using System.Threading;
65 | ///
66 | ///namespace SmartAnalyzers.MultithreadingAnalyzer.Test.CopiedSpinLock.TestData
67 | ///{
68 | /// class _001_SpinLockPassByValue
69 | /// {
70 | /// public _001_SpinLockPassByValue(SpinLock spinLock)
71 | /// {
72 | ///
73 | /// }
74 | ///
75 | /// public void SynchronizeWith(SpinLock spinLock)
76 | /// {
77 | ///
78 | /// }
79 | /// }
80 | ///
81 | /// class SampleSpinlockWrapper1
82 | /// {
83 | /// public SampleSpinlockWrapper1(ref SpinLock spinLock)
84 | /// {
85 | ///
86 | /// }
87 | ///
88 | /// public void SynchronizeWi [rest of string was truncated]";.
89 | ///
90 | internal static string _001_SpinLockPassByValue {
91 | get {
92 | return ResourceManager.GetString("_001_SpinLockPassByValue", resourceCulture);
93 | }
94 | }
95 |
96 | ///
97 | /// Looks up a localized string similar to using System.Threading;
98 | ///
99 | ///namespace SmartAnalyzers.MultithreadingAnalyzer.Test.CopiedSpinLock.TestData
100 | ///{
101 | /// class _002_ReadonlySpinLock
102 | /// {
103 | /// private readonly SpinLock InvalidSpinLock = new SpinLock();
104 | /// private SpinLock ValidSpinLock = new SpinLock();
105 | /// }
106 | ///}
107 | ///.
108 | ///
109 | internal static string _002_ReadonlySpinLock {
110 | get {
111 | return ResourceManager.GetString("_002_ReadonlySpinLock", resourceCulture);
112 | }
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 | *.VC.db
84 | *.VC.VC.opendb
85 |
86 | # Visual Studio profiler
87 | *.psess
88 | *.vsp
89 | *.vspx
90 | *.sap
91 |
92 | # TFS 2012 Local Workspace
93 | $tf/
94 |
95 | # Guidance Automation Toolkit
96 | *.gpState
97 |
98 | # ReSharper is a .NET coding add-in
99 | _ReSharper*/
100 | *.[Rr]e[Ss]harper
101 | *.DotSettings.user
102 |
103 | # JustCode is a .NET coding add-in
104 | .JustCode
105 |
106 | # TeamCity is a build add-in
107 | _TeamCity*
108 |
109 | # DotCover is a Code Coverage Tool
110 | *.dotCover
111 |
112 | # NCrunch
113 | _NCrunch_*
114 | .*crunch*.local.xml
115 | nCrunchTemp_*
116 |
117 | # MightyMoose
118 | *.mm.*
119 | AutoTest.Net/
120 |
121 | # Web workbench (sass)
122 | .sass-cache/
123 |
124 | # Installshield output folder
125 | [Ee]xpress/
126 |
127 | # DocProject is a documentation generator add-in
128 | DocProject/buildhelp/
129 | DocProject/Help/*.HxT
130 | DocProject/Help/*.HxC
131 | DocProject/Help/*.hhc
132 | DocProject/Help/*.hhk
133 | DocProject/Help/*.hhp
134 | DocProject/Help/Html2
135 | DocProject/Help/html
136 |
137 | # Click-Once directory
138 | publish/
139 |
140 | # Publish Web Output
141 | *.[Pp]ublish.xml
142 | *.azurePubxml
143 | # TODO: Comment the next line if you want to checkin your web deploy settings
144 | # but database connection strings (with potential passwords) will be unencrypted
145 | #*.pubxml
146 | *.publishproj
147 |
148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
149 | # checkin your Azure Web App publish settings, but sensitive information contained
150 | # in these scripts will be unencrypted
151 | PublishScripts/
152 |
153 | # NuGet Packages
154 | *.nupkg
155 | # The packages folder can be ignored because of Package Restore
156 | **/packages/*
157 | # except build/, which is used as an MSBuild target.
158 | !**/packages/build/
159 | # Uncomment if necessary however generally it will be regenerated when needed
160 | #!**/packages/repositories.config
161 | # NuGet v3's project.json files produces more ignoreable files
162 | *.nuget.props
163 | *.nuget.targets
164 |
165 | # Microsoft Azure Build Output
166 | csx/
167 | *.build.csdef
168 |
169 | # Microsoft Azure Emulator
170 | ecf/
171 | rcf/
172 |
173 | # Windows Store app package directories and files
174 | AppPackages/
175 | BundleArtifacts/
176 | Package.StoreAssociation.xml
177 | _pkginfo.txt
178 |
179 | # Visual Studio cache files
180 | # files ending in .cache can be ignored
181 | *.[Cc]ache
182 | # but keep track of directories ending in .cache
183 | !*.[Cc]ache/
184 |
185 | # Others
186 | ClientBin/
187 | ~$*
188 | *~
189 | *.dbmdl
190 | *.dbproj.schemaview
191 | *.pfx
192 | *.publishsettings
193 | node_modules/
194 | orleans.codegen.cs
195 |
196 | # Since there are multiple workflows, uncomment next line to ignore bower_components
197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
198 | #bower_components/
199 |
200 | # RIA/Silverlight projects
201 | Generated_Code/
202 |
203 | # Backup & report files from converting an old project file
204 | # to a newer Visual Studio version. Backup files are not needed,
205 | # because we have git ;-)
206 | _UpgradeReport_Files/
207 | Backup*/
208 | UpgradeLog*.XML
209 | UpgradeLog*.htm
210 |
211 | # SQL Server files
212 | *.mdf
213 | *.ldf
214 |
215 | # Business Intelligence projects
216 | *.rdl.data
217 | *.bim.layout
218 | *.bim_*.settings
219 |
220 | # Microsoft Fakes
221 | FakesAssemblies/
222 |
223 | # GhostDoc plugin setting file
224 | *.GhostDoc.xml
225 |
226 | # Node.js Tools for Visual Studio
227 | .ntvs_analysis.dat
228 |
229 | # Visual Studio 6 build log
230 | *.plg
231 |
232 | # Visual Studio 6 workspace options file
233 | *.opt
234 |
235 | # Visual Studio LightSwitch build output
236 | **/*.HTMLClient/GeneratedArtifacts
237 | **/*.DesktopClient/GeneratedArtifacts
238 | **/*.DesktopClient/ModelManifest.xml
239 | **/*.Server/GeneratedArtifacts
240 | **/*.Server/ModelManifest.xml
241 | _Pvt_Extensions
242 |
243 | # Paket dependency manager
244 | .paket/paket.exe
245 | paket-files/
246 |
247 | # FAKE - F# Make
248 | .fake/
249 |
250 | # JetBrains Rider
251 | .idea/
252 | *.sln.iml
253 | /src/Wealthenstein.Web/App_Data
254 | /src/Wealthenstein.Office.Web/App_Data
255 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/SmartAnalyzers.MultithreadingAnalyzer.Test.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | True
59 | True
60 | TestCases.resx
61 |
62 |
63 | True
64 | True
65 | TestCases.resx
66 |
67 |
68 | True
69 | True
70 | TestCases.resx
71 |
72 |
73 | True
74 | True
75 | TestCases.resx
76 |
77 |
78 | True
79 | True
80 | TestCases.resx
81 |
82 |
83 |
84 |
85 |
86 | ResXFileCodeGenerator
87 | TestCases.Designer.cs
88 |
89 |
90 | ResXFileCodeGenerator
91 | TestCases.Designer.cs
92 |
93 |
94 | ResXFileCodeGenerator
95 | TestCases.Designer.cs
96 |
97 |
98 | ResXFileCodeGenerator
99 | TestCases.Designer.cs
100 |
101 |
102 | ResXFileCodeGenerator
103 | TestCases.Designer.cs
104 |
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/LockObjectSelection/TestData/007_LockOnObjectWithWeakIdentity.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Reflection;
4 | using System.Threading;
5 |
6 | namespace SmartAnalyzers.MultithreadingAnalyzer.Test.LockObjectSelection.TestData
7 | {
8 | class _007_LockOnObjectWithWeakIdentity
9 | {
10 | private readonly Type lockType = typeof(_007_LockOnObjectWithWeakIdentity);
11 |
12 | public void DoSth1()
13 | {
14 | lock (typeof(_007_LockOnObjectWithWeakIdentity))
15 | {
16 |
17 | }
18 | }
19 |
20 | public void DoSth2()
21 | {
22 | lock (lockType)
23 | {
24 |
25 | }
26 | }
27 |
28 | public void DoSth3()
29 | {
30 | Monitor.Enter(lockType);
31 | Monitor.Exit(lockType);
32 | }
33 |
34 | public void DoSth4()
35 | {
36 | var wasTaken = false;
37 | Monitor.TryEnter(lockType, ref wasTaken);
38 | if (wasTaken)
39 | {
40 | Monitor.Exit(lockType);
41 | }
42 | }
43 |
44 | private readonly int[] lockValueArray = new int[10];
45 |
46 | public void DoSth5()
47 | {
48 | lock (lockValueArray)
49 | {
50 |
51 | }
52 | }
53 |
54 | public void DoSth6()
55 | {
56 | Monitor.Enter(lockValueArray);
57 | Monitor.Exit(lockValueArray);
58 | }
59 |
60 | public void DoSth7()
61 | {
62 | var wasTaken = false;
63 | Monitor.TryEnter(lockValueArray, ref wasTaken);
64 | if (wasTaken)
65 | {
66 | Monitor.Exit(lockValueArray);
67 | }
68 | }
69 |
70 | private readonly Thread lockThread = new Thread(() => {});
71 |
72 | public void DoSth8()
73 | {
74 | lock (lockThread)
75 | {
76 |
77 | }
78 | }
79 |
80 | public void DoSth9()
81 | {
82 | Monitor.Enter(lockThread);
83 | Monitor.Exit(lockThread);
84 | }
85 |
86 | public void DoSth10()
87 | {
88 | var wasTaken = false;
89 | Monitor.TryEnter(lockThread, ref wasTaken);
90 | if (wasTaken)
91 | {
92 | Monitor.Exit(lockThread);
93 | }
94 | }
95 |
96 | private readonly string lockTString = "sample";
97 |
98 | public void DoSth11()
99 | {
100 | lock (lockTString)
101 | {
102 |
103 | }
104 | }
105 |
106 | public void DoSth12()
107 | {
108 | Monitor.Enter(lockTString);
109 | Monitor.Exit(lockTString);
110 | }
111 |
112 | public void DoSth13()
113 | {
114 | var wasTaken = false;
115 | Monitor.TryEnter(lockTString, ref wasTaken);
116 | if (wasTaken)
117 | {
118 | Monitor.Exit(lockTString);
119 | }
120 | }
121 |
122 |
123 | private readonly ParameterInfo parameterInfoLock = typeof(_007_LockOnObjectWithWeakIdentity)
124 | .GetMethod(nameof(DoSth14)).GetParameters().First();
125 |
126 | public void DoSth14(int param)
127 | {
128 | lock (parameterInfoLock)
129 | {
130 |
131 | }
132 | }
133 |
134 | public void DoSth15()
135 | {
136 | Monitor.Enter(parameterInfoLock);
137 | Monitor.Exit(parameterInfoLock);
138 | }
139 |
140 | public void DoSth16()
141 | {
142 | var wasTaken = false;
143 | Monitor.TryEnter(parameterInfoLock, ref wasTaken);
144 | if (wasTaken)
145 | {
146 | Monitor.Exit(parameterInfoLock);
147 | }
148 | }
149 |
150 | public class Worker : MarshalByRefObject
151 | {
152 | }
153 |
154 | private readonly Worker marshalByRefLock = new Worker();
155 |
156 | public void DoSth17()
157 | {
158 | lock (marshalByRefLock)
159 | {
160 |
161 | }
162 | }
163 |
164 | public void DoSth18()
165 | {
166 | Monitor.Enter(marshalByRefLock);
167 | Monitor.Exit(marshalByRefLock);
168 | }
169 |
170 | public void DoSth19()
171 | {
172 | var wasTaken = false;
173 | Monitor.TryEnter(marshalByRefLock, ref wasTaken);
174 | if (wasTaken)
175 | {
176 | Monitor.Exit(marshalByRefLock);
177 | }
178 | }
179 |
180 | private readonly Exception exceptionLock = new OutOfMemoryException();
181 |
182 | public void DoSth20()
183 | {
184 | lock (exceptionLock)
185 | {
186 |
187 | }
188 | }
189 |
190 | public void DoSth21()
191 | {
192 | Monitor.Enter(exceptionLock);
193 | Monitor.Exit(exceptionLock);
194 | }
195 |
196 | public void DoSth22()
197 | {
198 | var wasTaken = false;
199 | Monitor.TryEnter(exceptionLock, ref wasTaken);
200 | if (wasTaken)
201 | {
202 | Monitor.Exit(exceptionLock);
203 | }
204 | }
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer/AbandonLockAnalyzer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Immutable;
3 | using System.Threading;
4 | using Microsoft.CodeAnalysis;
5 | using Microsoft.CodeAnalysis.CSharp;
6 | using Microsoft.CodeAnalysis.CSharp.Syntax;
7 | using Microsoft.CodeAnalysis.Diagnostics;
8 | using SmartAnalyzers.MultithreadingAnalyzer.Utils;
9 |
10 | namespace SmartAnalyzers.MultithreadingAnalyzer
11 | {
12 | [DiagnosticAnalyzer(LanguageNames.CSharp)]
13 | public class AbandonLockAnalyzer : DiagnosticAnalyzer
14 | {
15 | internal const string Category = "Locking";
16 |
17 | public static readonly DiagnosticDescriptor MT1012 = new DiagnosticDescriptor(nameof(MT1012), "Acquiring lock without guarantee of releasing", (LocalizableString) "Acquiring lock should always be wrapped in try block to ensure execution", Category, DiagnosticSeverity.Error, true);
18 | public static readonly DiagnosticDescriptor MT1013 = new DiagnosticDescriptor(nameof(MT1013), "Releasing lock without guarantee of execution", (LocalizableString) "Releasing lock should always be wrapped in finally block to ensure execution", Category, DiagnosticSeverity.Error, true);
19 |
20 | public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(MT1012, MT1013);
21 |
22 | public override void Initialize(AnalysisContext context)
23 | {
24 | if (context == null)
25 | throw new ArgumentNullException(nameof(context));
26 |
27 | context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
28 | context.EnableConcurrentExecution();
29 | context.RegisterSyntaxNodeAction(AnalyzeMonitorMethodInvocation, SyntaxKind.InvocationExpression);
30 | }
31 |
32 | private static readonly MethodDescriptor[] MethodsThatRequireFinally =
33 | {
34 | new MethodDescriptor("System.Threading.Monitor.Exit"),
35 | new MethodDescriptor("System.Threading.SpinLock.Exit"),
36 | new MethodDescriptor("System.Threading.Mutex.ReleaseMutex"),
37 | new MethodDescriptor("System.Threading.ReaderWriterLockSlim.ExitWriteLock"),
38 | new MethodDescriptor("System.Threading.ReaderWriterLockSlim.ExitReadLock"),
39 | new MethodDescriptor("System.Threading.ReaderWriterLockSlim.ExitUpgradeableReadLock"),
40 | new MethodDescriptor("System.Threading.ReaderWriterLock.ReleaseReaderLock"),
41 | new MethodDescriptor("System.Threading.ReaderWriterLock.ReleaseWriterLock"),
42 | };
43 |
44 | private static readonly MethodDescriptor[] MethodsThatRequireTry =
45 | {
46 | new MethodDescriptor("System.Threading.Monitor.Enter"),
47 | new MethodDescriptor("System.Threading.Monitor.TryEnter"),
48 | new MethodDescriptor("System.Threading.SpinLock.Enter"),
49 | new MethodDescriptor("System.Threading.SpinLock.TryEnter"),
50 | new MethodDescriptor("System.Threading.Mutex.WaitOne"),
51 | new MethodDescriptor("System.Threading.ReaderWriterLockSlim.EnterWriteLock"),
52 | new MethodDescriptor("System.Threading.ReaderWriterLockSlim.EnterReadLock"),
53 | new MethodDescriptor("System.Threading.ReaderWriterLockSlim.EnterUpgradeableReadLock"),
54 | new MethodDescriptor("System.Threading.ReaderWriterLock.AcquireReaderLock"),
55 | new MethodDescriptor("System.Threading.ReaderWriterLock.AcquireWriterLock"),
56 | };
57 |
58 | private void AnalyzeMonitorMethodInvocation(SyntaxNodeAnalysisContext context)
59 | {
60 | var invocationExpression = (InvocationExpressionSyntax)context.Node;
61 | if (ExpressionHelpers.IsInvocationOf(context, MethodsThatRequireFinally))
62 | {
63 | TryReportLockReleaseWithoutExecutionGuarantee(context, invocationExpression.Parent, invocationExpression);
64 | }else if (ExpressionHelpers.IsInvocationOf(context, MethodsThatRequireTry))
65 | {
66 | TryReportLockAcquiredWithoutReleaseGuarantee(context, invocationExpression.Parent, invocationExpression);
67 | }
68 | }
69 |
70 | private void TryReportLockAcquiredWithoutReleaseGuarantee(SyntaxNodeAnalysisContext context, SyntaxNode parentSyntaxNode, InvocationExpressionSyntax acquiredExpression)
71 | {
72 | if (parentSyntaxNode is TryStatementSyntax)
73 | {
74 | return;
75 | }
76 | if (parentSyntaxNode is MethodDeclarationSyntax methodDeclaration && methodDeclaration.Parent is TypeDeclarationSyntax)
77 | {
78 | context.ReportDiagnostic(Diagnostic.Create(MT1012, acquiredExpression.GetLocation()));
79 | }
80 | else if (parentSyntaxNode.Parent != null)
81 | {
82 | TryReportLockAcquiredWithoutReleaseGuarantee(context, parentSyntaxNode.Parent, acquiredExpression);
83 | }
84 | }
85 |
86 | private void TryReportLockReleaseWithoutExecutionGuarantee(SyntaxNodeAnalysisContext context, SyntaxNode parentSyntaxNode, SyntaxNode releaseExpression)
87 | {
88 | if (parentSyntaxNode is FinallyClauseSyntax)
89 | {
90 | return;
91 | }
92 | if (parentSyntaxNode is MethodDeclarationSyntax methodDeclaration && methodDeclaration.Parent is TypeDeclarationSyntax)
93 | {
94 | context.ReportDiagnostic(Diagnostic.Create(MT1013, releaseExpression.GetLocation()));
95 | }
96 | else if(parentSyntaxNode.Parent !=null)
97 | {
98 | TryReportLockReleaseWithoutExecutionGuarantee(context, parentSyntaxNode.Parent, releaseExpression);
99 | }
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/DeprecatedReaderWriterLock/TestCases.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | TestData\001_ReaderWriterLock.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
123 |
124 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer/LockObjectSelectionAnalyzer.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Immutable;
2 | using Microsoft.CodeAnalysis;
3 | using Microsoft.CodeAnalysis.CSharp;
4 | using Microsoft.CodeAnalysis.CSharp.Syntax;
5 | using Microsoft.CodeAnalysis.Diagnostics;
6 |
7 | namespace SmartAnalyzers.MultithreadingAnalyzer
8 | {
9 | [DiagnosticAnalyzer(LanguageNames.CSharp)]
10 | public partial class LockObjectSelectionAnalyzer : BasicLockAnalyzer
11 | {
12 | const string Category = "Locking";
13 | public static readonly DiagnosticDescriptor MT1000 = new DiagnosticDescriptor(nameof(MT1000), "Lock on publicly accessible member", "Locking on publicly accessible member can cause a deadlock'", Category, DiagnosticSeverity.Error, true);
14 | public static readonly DiagnosticDescriptor MT1001 = new DiagnosticDescriptor(nameof(MT1001), "Lock on this reference", "Locking on this reference can cause a deadlock", Category, DiagnosticSeverity.Error, true);
15 | public static readonly DiagnosticDescriptor MT1002 = new DiagnosticDescriptor(nameof(MT1002), "Lock on object with weak identity", "Locking on object with weak identity can cause a deadlock because their are implicitly shared across the application", Category, DiagnosticSeverity.Error, true);
16 | public static readonly DiagnosticDescriptor MT1003 = new DiagnosticDescriptor(nameof(MT1003), "Lock on non-readonly member", "Locking on non-readonly member can cause a deadlock'", Category, DiagnosticSeverity.Error, true);
17 | public static readonly DiagnosticDescriptor MT1004 = new DiagnosticDescriptor(nameof(MT1004), "Lock on value type instance", "Locking on value types prohibits any synchronization because they are boxed", Category, DiagnosticSeverity.Error, true);
18 |
19 | public override ImmutableArray SupportedDiagnostics =>
20 | ImmutableArray.Create(MT1001, MT1000, MT1003, MT1004, MT1002);
21 |
22 | protected override void TryToReportViolation(SyntaxNodeAnalysisContext context, ExpressionSyntax expression)
23 | {
24 | if (expression == null)
25 | {
26 | return;
27 | }
28 |
29 | var expressionKind = expression.Kind();
30 |
31 | if (IsLockOnThisReferenceViolated(expressionKind))
32 | {
33 | ReportViolation(context, expression, MT1001);
34 | return;
35 | }
36 |
37 | var symbolInfo = context.SemanticModel.GetSymbolInfo(expression);
38 |
39 | if (IsLockOnNonReadonlyViolated(expressionKind, symbolInfo))
40 | {
41 | ReportViolation(context, expression, MT1003);
42 | }
43 |
44 | if (IsLockPubliclyAccessibleMemberViolated(expressionKind, symbolInfo))
45 | {
46 | ReportViolation(context, expression, MT1000);
47 | }
48 |
49 | var typeInfo = context.SemanticModel.GetTypeInfo(expression);
50 |
51 | if (IsLockOnValueTypeViolated(typeInfo))
52 | {
53 | ReportViolation(context, expression, MT1004);
54 | return;
55 | }
56 |
57 | if (IsLockOnObjectWithWeakIdentity(typeInfo))
58 | {
59 | ReportViolation(context, expression, MT1002);
60 | return;
61 | }
62 | }
63 |
64 | private bool IsLockOnObjectWithWeakIdentity(TypeInfo typeInfo)
65 | {
66 | switch (typeInfo.Type.TypeKind)
67 | {
68 | case TypeKind.Array:
69 | return typeInfo.Type is IArrayTypeSymbol arrayType && SymbolHelper.IsPrimitiveType(arrayType.ElementType);
70 | case TypeKind.Class:
71 | case TypeKind.TypeParameter:
72 | return
73 | typeInfo.Type.SpecialType == SpecialType.System_String ||
74 | SymbolHelper.CanBeAssignedTo(typeInfo.Type, "System.Exception") ||
75 | SymbolHelper.CanBeAssignedTo(typeInfo.Type, "System.MarshalByRefObject") ||
76 | SymbolHelper.CanBeAssignedTo(typeInfo.Type, "System.Reflection.MemberInfo") ||
77 | SymbolHelper.CanBeAssignedTo(typeInfo.Type, "System.Reflection.ParameterInfo") ||
78 | SymbolHelper.CanBeAssignedTo(typeInfo.Type, "System.Runtime.ConstrainedExecution.CriticalFinalizerObject");
79 | default:
80 | return false;
81 | }
82 | }
83 |
84 | private static bool IsLockOnValueTypeViolated(TypeInfo typeInfo)
85 | {
86 | return typeInfo.Type != null && typeInfo.Type.IsValueType;
87 | }
88 |
89 | private static void ReportViolation(SyntaxNodeAnalysisContext context, ExpressionSyntax expression, DiagnosticDescriptor rule)
90 | {
91 | context.ReportDiagnostic(Diagnostic.Create(rule, expression.GetLocation()));
92 | }
93 |
94 | private static bool IsLockPubliclyAccessibleMemberViolated(SyntaxKind expressionKind, SymbolInfo symbolInfo)
95 | {
96 | if (expressionKind == SyntaxKind.SimpleMemberAccessExpression || expressionKind == SyntaxKind.IdentifierName)
97 | {
98 | if (symbolInfo.Symbol is IPropertySymbol || symbolInfo.Symbol is IFieldSymbol ||
99 | symbolInfo.Symbol is IEventSymbol)
100 | {
101 | if (symbolInfo.Symbol.DeclaredAccessibility != Accessibility.Private)
102 | {
103 | return true;
104 | }
105 | }
106 | }
107 | return false;
108 | }
109 |
110 | private static bool IsLockOnThisReferenceViolated(SyntaxKind expressionKind)
111 | {
112 | return expressionKind == SyntaxKind.ThisExpression;
113 | }
114 |
115 | private static bool IsLockOnNonReadonlyViolated(SyntaxKind expressionKind, SymbolInfo symbolInfo)
116 | {
117 | if (expressionKind == SyntaxKind.SimpleMemberAccessExpression || expressionKind == SyntaxKind.IdentifierName)
118 | {
119 | if ((symbolInfo.Symbol is IFieldSymbol fieldSymbol && fieldSymbol.IsReadOnly == false) ||
120 | (symbolInfo.Symbol is IPropertySymbol propertySymbol && propertySymbol.IsReadOnly == false))
121 | {
122 | return true;
123 | }
124 | }
125 |
126 | return false;
127 | }
128 | }
129 | }
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/MethodLevelSynchronization/TestCases.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | TestData\001_MethodLevelSyncrhonization.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
123 |
124 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/CopiedSpinLock/TestCases.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | TestData\001_SpinLockPassByValue.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
123 |
124 |
125 | TestData\002_ReadonlySpinLock.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
126 |
127 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/AbandonLock/TestCases.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace SmartAnalyzers.MultithreadingAnalyzer.Test.AbandonLock {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class TestCases {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal TestCases() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SmartAnalyzers.MultithreadingAnalyzer.Test.AbandonLock.TestCases", typeof(TestCases).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized string similar to using System.Threading;
65 | ///
66 | ///namespace SmartAnalyzers.MultithreadingAnalyzer.Test.AbandonLock.TestData
67 | ///{
68 | /// class _001_LockAcquiredOutsideTryClause
69 | /// {
70 | /// private readonly object myLockObj = new object();
71 | ///
72 | /// public void DoSth1()
73 | /// {
74 | /// Monitor.Enter(myLockObj);
75 | /// try
76 | /// {
77 | ///
78 | /// }
79 | /// finally
80 | /// {
81 | /// Monitor.Exit(myLockObj);
82 | /// }
83 | /// }
84 | ///
85 | /// public void DoSth2()
86 | /// {
87 | /// var lo [rest of string was truncated]";.
88 | ///
89 | internal static string _001_LockAcquiredOutsideTryClause {
90 | get {
91 | return ResourceManager.GetString("_001_LockAcquiredOutsideTryClause", resourceCulture);
92 | }
93 | }
94 |
95 | ///
96 | /// Looks up a localized string similar to using System.Threading;
97 | ///
98 | ///namespace SmartAnalyzers.MultithreadingAnalyzer.Test.AbandonLock.TestData
99 | ///{
100 | /// class _002_LockAcquiredInsideTryClause
101 | /// {
102 | /// private readonly object myLockObj = new object();
103 | ///
104 | /// public void DoSth1()
105 | /// {
106 | /// try
107 | /// {
108 | /// Monitor.Enter(myLockObj);
109 | /// }
110 | /// finally
111 | /// {
112 | /// Monitor.Exit(myLockObj);
113 | /// }
114 | /// }
115 | ///
116 | /// public void DoSth2()
117 | /// {
118 | /// var l [rest of string was truncated]";.
119 | ///
120 | internal static string _002_LockAcquiredInsideTryClause {
121 | get {
122 | return ResourceManager.GetString("_002_LockAcquiredInsideTryClause", resourceCulture);
123 | }
124 | }
125 |
126 | ///
127 | /// Looks up a localized string similar to using System.Threading;
128 | ///
129 | ///namespace SmartAnalyzers.MultithreadingAnalyzer.Test.AbandonLock.TestData
130 | ///{
131 | /// class _003_LockReleasedOutsideFinallyClause
132 | /// {
133 | /// private readonly object myLockObj = new object();
134 | ///
135 | /// public void DoSth1()
136 | /// {
137 | /// Monitor.Enter(myLockObj);
138 | /// try
139 | /// {
140 | ///
141 | /// }
142 | /// finally
143 | /// {
144 | /// Monitor.Exit(myLockObj);
145 | /// }
146 | /// }
147 | ///
148 | /// public void DoSth2()
149 | /// {
150 | /// va [rest of string was truncated]";.
151 | ///
152 | internal static string _003_LockReleasedOutsideFinallyClause {
153 | get {
154 | return ResourceManager.GetString("_003_LockReleasedOutsideFinallyClause", resourceCulture);
155 | }
156 | }
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/AbandonLock/TestCases.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | TestData\001_LockAcquiredOutsideTryClause.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
123 |
124 |
125 | TestData\002_LockAcquiredInsideTryClause.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
126 |
127 |
128 | TestData\003_LockReleasedOutsideFinallyClause.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
129 |
130 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/AbandonLock/AbandonLockAnalyzerTest.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading;
3 | using Microsoft.CodeAnalysis;
4 | using Microsoft.CodeAnalysis.Diagnostics;
5 | using NUnit.Framework;
6 | using RoslynTestKit;
7 |
8 | namespace SmartAnalyzers.MultithreadingAnalyzer.Test.AbandonLock
9 | {
10 | public class AbandonLockAnalyzerTest : AnalyzerTestFixture
11 | {
12 | protected override string LanguageName => LanguageNames.CSharp;
13 | protected override DiagnosticAnalyzer CreateAnalyzer() => new AbandonLockAnalyzer();
14 | protected override IReadOnlyCollection References => new[]
15 | {
16 | ReferenceSource.FromType(),
17 | };
18 |
19 | [Test]
20 | public void should_report_monitor_enter_outside_the_try_clause()
21 | => HasDiagnosticAtLine(TestCases._001_LockAcquiredOutsideTryClause, nameof(AbandonLockAnalyzer.MT1012), 11);
22 |
23 | [Test]
24 | public void should_report_monitor_try_enter_outside_the_try_clause()
25 | => HasDiagnosticAtLine(TestCases._001_LockAcquiredOutsideTryClause, nameof(AbandonLockAnalyzer.MT1012), 24);
26 |
27 | [Test]
28 | public void should_report_spinlock_enter_outside_the_try_clause()
29 | => HasDiagnosticAtLine(TestCases._001_LockAcquiredOutsideTryClause, nameof(AbandonLockAnalyzer.MT1012), 41);
30 |
31 | [Test]
32 | public void should_report_spinlock_try_enter_outside_the_try_clause()
33 | => HasDiagnosticAtLine(TestCases._001_LockAcquiredOutsideTryClause, nameof(AbandonLockAnalyzer.MT1012), 56);
34 |
35 | [Test]
36 | public void should_report_mutext_wait_one_outside_the_try_clause()
37 | => HasDiagnosticAtLine(TestCases._001_LockAcquiredOutsideTryClause, nameof(AbandonLockAnalyzer.MT1012), 72);
38 |
39 | [Test]
40 | public void should_report_readerwriterlockslim_enter_read_lock_outside_the_try_clause()
41 | => HasDiagnosticAtLine(TestCases._001_LockAcquiredOutsideTryClause, nameof(AbandonLockAnalyzer.MT1012), 87);
42 |
43 | [Test]
44 | public void should_report_readerwriterlockslim_enter_write_lock_outside_the_try_clause()
45 | => HasDiagnosticAtLine(TestCases._001_LockAcquiredOutsideTryClause, nameof(AbandonLockAnalyzer.MT1012), 100);
46 |
47 | [Test]
48 | public void should_report_readerwriterlockslim_enter_upgradable_read_lock_outside_the_try_clause()
49 | => HasDiagnosticAtLine(TestCases._001_LockAcquiredOutsideTryClause, nameof(AbandonLockAnalyzer.MT1012), 113);
50 |
51 |
52 | [Test]
53 | public void should_report_readerwriterlock_enter_read_lock_outside_the_try_clause()
54 | => HasDiagnosticAtLine(TestCases._001_LockAcquiredOutsideTryClause, nameof(AbandonLockAnalyzer.MT1012), 128);
55 |
56 | [Test]
57 | public void should_report_readerwriterlock_enter_write_lock_outside_the_try_clause()
58 | => HasDiagnosticAtLine(TestCases._001_LockAcquiredOutsideTryClause, nameof(AbandonLockAnalyzer.MT1012), 141);
59 |
60 | [Test]
61 | public void should_not_report_lock_acquired_inside_the_try_clause()
62 | => NoDiagnostic(TestCases._002_LockAcquiredInsideTryClause, nameof(AbandonLockAnalyzer.MT1012));
63 |
64 | [Test]
65 | public void should_not_report_monitor_enter_inside_the_try_clause()
66 | => NoDiagnosticAtLine(TestCases._002_LockAcquiredInsideTryClause, nameof(AbandonLockAnalyzer.MT1012), 13);
67 |
68 | [Test]
69 | public void should_not_report_monitor_try_enter_inside_the_try_clause()
70 | => NoDiagnosticAtLine(TestCases._002_LockAcquiredInsideTryClause, nameof(AbandonLockAnalyzer.MT1012), 26);
71 |
72 | [Test]
73 | public void should_not_report_spinlock_enter_inside_the_try_clause()
74 | => NoDiagnosticAtLine(TestCases._002_LockAcquiredInsideTryClause, nameof(AbandonLockAnalyzer.MT1012), 42);
75 |
76 | [Test]
77 | public void should_not_report_spinlock_try_enter_inside_the_try_clause()
78 | => NoDiagnosticAtLine(TestCases._002_LockAcquiredInsideTryClause, nameof(AbandonLockAnalyzer.MT1012), 56);
79 |
80 | [Test]
81 | public void should_not_report_mutext_wait_one_inside_the_try_clause()
82 | => NoDiagnosticAtLine(TestCases._002_LockAcquiredInsideTryClause, nameof(AbandonLockAnalyzer.MT1012), 71);
83 |
84 | [Test]
85 | public void should_not_report_readerwriterlockslim_enter_read_lock_inside_the_try_clause()
86 | => NoDiagnosticAtLine(TestCases._002_LockAcquiredInsideTryClause, nameof(AbandonLockAnalyzer.MT1012), 85);
87 |
88 | [Test]
89 | public void should_not_report_readerwriterlockslim_enter_write_lock_inside_the_try_clause()
90 | => NoDiagnosticAtLine(TestCases._002_LockAcquiredInsideTryClause, nameof(AbandonLockAnalyzer.MT1012), 97);
91 |
92 | [Test]
93 | public void should_not_report_readerwriterlockslim_enter_upgradable_read_lock_inside_the_try_clause()
94 | => NoDiagnosticAtLine(TestCases._002_LockAcquiredInsideTryClause, nameof(AbandonLockAnalyzer.MT1012), 109);
95 |
96 |
97 | [Test]
98 | public void should_not_report_readerwriterlock_enter_read_lock_inside_the_try_clause()
99 | => NoDiagnosticAtLine(TestCases._002_LockAcquiredInsideTryClause, nameof(AbandonLockAnalyzer.MT1012), 123);
100 |
101 | [Test]
102 | public void should_not_report_readerwriterlock_enter_write_lock_inside_the_try_clause()
103 | => NoDiagnosticAtLine(TestCases._002_LockAcquiredInsideTryClause, nameof(AbandonLockAnalyzer.MT1012), 135);
104 |
105 | [Test]
106 | public void should_not_report_lock_release_inside_the_finally_clause()
107 | => NoDiagnostic(TestCases._002_LockAcquiredInsideTryClause, nameof(AbandonLockAnalyzer.MT1013));
108 |
109 | [Test]
110 | public void should_report_monitor_exit_outside_the_finally_clause()
111 | => HasDiagnosticAtLine(TestCases._003_LockReleasedOutsideFinallyClause, nameof(AbandonLockAnalyzer.MT1013), 12);
112 |
113 | [Test]
114 | public void should_report_spinlock_exit_outside_the_finally_clause()
115 | => HasDiagnosticAtLine(TestCases._003_LockReleasedOutsideFinallyClause, nameof(AbandonLockAnalyzer.MT1013), 22);
116 |
117 | [Test]
118 | public void should_report_mutext_release_outside_the_finally_clause()
119 | => HasDiagnosticAtLine(TestCases._003_LockReleasedOutsideFinallyClause, nameof(AbandonLockAnalyzer.MT1013), 30);
120 |
121 | [Test]
122 | public void should_report_readerwriterlockslim_exit_read_lock_outside_the_finally_clause()
123 | => HasDiagnosticAtLine(TestCases._003_LockReleasedOutsideFinallyClause, nameof(AbandonLockAnalyzer.MT1013), 38);
124 |
125 | [Test]
126 | public void should_report_readerwriterlockslim_exit_write_lock_outside_the_finally_clause()
127 | => HasDiagnosticAtLine(TestCases._003_LockReleasedOutsideFinallyClause, nameof(AbandonLockAnalyzer.MT1013), 44);
128 |
129 | [Test]
130 | public void should_report_readerwriterlockslim_exit_upgradable_read_lock_outside_the_finally_clause()
131 | => HasDiagnosticAtLine(TestCases._003_LockReleasedOutsideFinallyClause, nameof(AbandonLockAnalyzer.MT1013), 50);
132 |
133 |
134 | [Test]
135 | public void should_report_readerwriterlock_exit_read_lock_outside_the_finally_clause()
136 | => HasDiagnosticAtLine(TestCases._003_LockReleasedOutsideFinallyClause, nameof(AbandonLockAnalyzer.MT1013), 58);
137 |
138 | [Test]
139 | public void should_report_readerwriterlock_exit_write_lock_outside_the_finally_clause()
140 | => HasDiagnosticAtLine(TestCases._003_LockReleasedOutsideFinallyClause, nameof(AbandonLockAnalyzer.MT1013), 64);
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/LockObjectSelection/TestCases.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | TestData\001_LockOnNonReadonlyField.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
123 |
124 |
125 | TestData\002_LockOnNonReadonlyProperty.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
126 |
127 |
128 | TestData\003_LockOnPublicField.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
129 |
130 |
131 | TestData\004_LockOnPublicProperty.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
132 |
133 |
134 | TestData\005_LockOnThisInstance.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
135 |
136 |
137 | TestData\006_LockOnValueType.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
138 |
139 |
140 | TestData\007_LockOnObjectWithWeakIdentity.cs;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
141 |
142 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/LockObjectSelection/LockObjectSelectionAnalyzerTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using Microsoft.CodeAnalysis.Diagnostics;
3 | using NUnit.Framework;
4 | using RoslynTestKit;
5 |
6 | namespace SmartAnalyzers.MultithreadingAnalyzer.Test.LockObjectSelection
7 | {
8 | public class LockObjectSelectionAnalyzerTests: AnalyzerTestFixture
9 | {
10 | [Test]
11 | public void do_not_lock_on_non_readonly_field()
12 | {
13 | HasDiagnosticAtLine(TestCases._001_LockOnNonReadonlyField, nameof(LockObjectSelectionAnalyzer.MT1003), 11);
14 | }
15 |
16 | [Test]
17 | public void do_not_monitor_enter_on_non_readonly_field()
18 | {
19 | HasDiagnosticAtLine(TestCases._001_LockOnNonReadonlyField, nameof(LockObjectSelectionAnalyzer.MT1003), 18);
20 | }
21 |
22 | [Test]
23 | public void do_not_monitor_try_enter_on_non_readonly_field()
24 | {
25 | HasDiagnosticAtLine(TestCases._001_LockOnNonReadonlyField, nameof(LockObjectSelectionAnalyzer.MT1003), 25);
26 | }
27 |
28 | [Test]
29 | public void do_not_lock_on_non_readonly_property()
30 | {
31 | HasDiagnosticAtLine(TestCases._002_LockOnNonReadonlyProperty, nameof(LockObjectSelectionAnalyzer.MT1003), 11);
32 | }
33 |
34 | [Test]
35 | public void do_not_monitor_enter_on_non_readonly_property()
36 | {
37 | HasDiagnosticAtLine(TestCases._002_LockOnNonReadonlyProperty, nameof(LockObjectSelectionAnalyzer.MT1003), 18);
38 | }
39 |
40 | [Test]
41 | public void do_not_monitor_try_enter_on_non_readonly_property()
42 | {
43 | HasDiagnosticAtLine(TestCases._002_LockOnNonReadonlyProperty, nameof(LockObjectSelectionAnalyzer.MT1003), 25);
44 | }
45 |
46 | [Test]
47 | public void do_not_lock_on_public_field()
48 | {
49 | HasDiagnosticAtLine(TestCases._003_LockOnPublicField, nameof(LockObjectSelectionAnalyzer.MT1000), 11);
50 | }
51 |
52 | [Test]
53 | public void do_not_monitor_enter_on_public_field()
54 | {
55 | HasDiagnosticAtLine(TestCases._003_LockOnPublicField, nameof(LockObjectSelectionAnalyzer.MT1000), 18);
56 | }
57 |
58 | [Test]
59 | public void do_not_monitor_try_enter_on_public_field()
60 | {
61 | HasDiagnosticAtLine(TestCases._003_LockOnPublicField, nameof(LockObjectSelectionAnalyzer.MT1000), 25);
62 | }
63 |
64 | [Test]
65 | public void do_not_lock_on_public_property()
66 | {
67 | HasDiagnosticAtLine(TestCases._004_LockOnPublicProperty, nameof(LockObjectSelectionAnalyzer.MT1000), 11);
68 | }
69 |
70 | [Test]
71 | public void do_not_monitor_enter_on_public_property()
72 | {
73 | HasDiagnosticAtLine(TestCases._004_LockOnPublicProperty, nameof(LockObjectSelectionAnalyzer.MT1000), 18);
74 | }
75 |
76 | [Test]
77 | public void do_not_monitor_try_enter_on_public_property()
78 | {
79 | HasDiagnosticAtLine(TestCases._004_LockOnPublicProperty, nameof(LockObjectSelectionAnalyzer.MT1000), 25);
80 | }
81 |
82 |
83 | [Test]
84 | public void do_not_monitor_enter_on_this_reference()
85 | {
86 | HasDiagnosticAtLine(TestCases._005_LockOnThisInstance, nameof(LockObjectSelectionAnalyzer.MT1001), 10);
87 | }
88 |
89 | [Test]
90 | public void do_not_monitor_try_enter_on_this_reference()
91 | {
92 | HasDiagnosticAtLine(TestCases._005_LockOnThisInstance, nameof(LockObjectSelectionAnalyzer.MT1001), 17);
93 | }
94 |
95 | [Test]
96 | public void do_not_monitor_enter_on_value_type()
97 | {
98 | HasDiagnosticAtLine(TestCases._006_LockOnValueType, nameof(LockObjectSelectionAnalyzer.MT1004), 16);
99 | }
100 |
101 | [Test]
102 | public void do_not_monitor_try_enter_on_value_type()
103 | {
104 | HasDiagnosticAtLine(TestCases._006_LockOnValueType, nameof(LockObjectSelectionAnalyzer.MT1004), 23);
105 | }
106 |
107 | [Test]
108 | public void do_not_lock_on_typeof()
109 | {
110 | HasDiagnosticAtLine(TestCases._007_LockOnObjectWithWeakIdentity, nameof(LockObjectSelectionAnalyzer.MT1002), 14);
111 | }
112 |
113 | [Test]
114 | public void do_not_lock_on_type_object()
115 | {
116 | HasDiagnosticAtLine(TestCases._007_LockOnObjectWithWeakIdentity, nameof(LockObjectSelectionAnalyzer.MT1002), 22);
117 | }
118 |
119 | [Test]
120 | public void do_not_monitor_on_type_object()
121 | {
122 | HasDiagnosticAtLine(TestCases._007_LockOnObjectWithWeakIdentity, nameof(LockObjectSelectionAnalyzer.MT1002), 30);
123 | }
124 |
125 | [Test]
126 | public void do_not_monitor_try_on_type_object()
127 | {
128 | HasDiagnosticAtLine(TestCases._007_LockOnObjectWithWeakIdentity, nameof(LockObjectSelectionAnalyzer.MT1002), 37);
129 | }
130 |
131 | [Test]
132 | public void do_not_lock_on_value_array()
133 | {
134 | HasDiagnosticAtLine(TestCases._007_LockOnObjectWithWeakIdentity, nameof(LockObjectSelectionAnalyzer.MT1002), 48);
135 | }
136 |
137 | [Test]
138 | public void do_not_monitor_on_value_array()
139 | {
140 | HasDiagnosticAtLine(TestCases._007_LockOnObjectWithWeakIdentity, nameof(LockObjectSelectionAnalyzer.MT1002), 56);
141 | }
142 |
143 | [Test]
144 | public void do_not_monitor_try_on_value_array()
145 | {
146 | HasDiagnosticAtLine(TestCases._007_LockOnObjectWithWeakIdentity, nameof(LockObjectSelectionAnalyzer.MT1002), 63);
147 | }
148 |
149 | [Test]
150 | public void do_not_lock_on_thread()
151 | {
152 | HasDiagnosticAtLine(TestCases._007_LockOnObjectWithWeakIdentity, nameof(LockObjectSelectionAnalyzer.MT1002), 74);
153 | }
154 |
155 | [Test]
156 | public void do_not_monitor_on_thread()
157 | {
158 | HasDiagnosticAtLine(TestCases._007_LockOnObjectWithWeakIdentity, nameof(LockObjectSelectionAnalyzer.MT1002), 82);
159 | }
160 |
161 | [Test]
162 | public void do_not_monitor_try_on_thread()
163 | {
164 | HasDiagnosticAtLine(TestCases._007_LockOnObjectWithWeakIdentity, nameof(LockObjectSelectionAnalyzer.MT1002), 89);
165 | }
166 |
167 | [Test]
168 | public void do_not_lock_on_string()
169 | {
170 | HasDiagnosticAtLine(TestCases._007_LockOnObjectWithWeakIdentity, nameof(LockObjectSelectionAnalyzer.MT1002), 100);
171 | }
172 |
173 | [Test]
174 | public void do_not_monitor_on_string()
175 | {
176 | HasDiagnosticAtLine(TestCases._007_LockOnObjectWithWeakIdentity, nameof(LockObjectSelectionAnalyzer.MT1002), 108);
177 | }
178 |
179 | [Test]
180 | public void do_not_monitor_try_on_string()
181 | {
182 | HasDiagnosticAtLine(TestCases._007_LockOnObjectWithWeakIdentity, nameof(LockObjectSelectionAnalyzer.MT1002), 115);
183 | }
184 |
185 | [Test]
186 | public void do_not_lock_on_parameter_info()
187 | {
188 | HasDiagnosticAtLine(TestCases._007_LockOnObjectWithWeakIdentity, nameof(LockObjectSelectionAnalyzer.MT1002), 128);
189 | }
190 |
191 | [Test]
192 | public void do_not_monitor_on_parameter_info()
193 | {
194 | HasDiagnosticAtLine(TestCases._007_LockOnObjectWithWeakIdentity, nameof(LockObjectSelectionAnalyzer.MT1002), 136);
195 | }
196 |
197 | [Test]
198 | public void do_not_monitor_try_on_parameter_info()
199 | {
200 | HasDiagnosticAtLine(TestCases._007_LockOnObjectWithWeakIdentity, nameof(LockObjectSelectionAnalyzer.MT1002), 143);
201 | }
202 |
203 | [Test]
204 | public void do_not_lock_on_marshal_by_ref()
205 | {
206 | HasDiagnosticAtLine(TestCases._007_LockOnObjectWithWeakIdentity, nameof(LockObjectSelectionAnalyzer.MT1002), 158);
207 | }
208 |
209 | [Test]
210 | public void do_not_monitor_on_marshal_by_ref()
211 | {
212 | HasDiagnosticAtLine(TestCases._007_LockOnObjectWithWeakIdentity, nameof(LockObjectSelectionAnalyzer.MT1002), 166);
213 | }
214 |
215 | [Test]
216 | public void do_not_monitor_try_on_marshal_by_ref()
217 | {
218 | HasDiagnosticAtLine(TestCases._007_LockOnObjectWithWeakIdentity, nameof(LockObjectSelectionAnalyzer.MT1002), 184);
219 | }
220 |
221 | [Test]
222 | public void do_not_lock_on_exception()
223 | {
224 | HasDiagnosticAtLine(TestCases._007_LockOnObjectWithWeakIdentity, nameof(LockObjectSelectionAnalyzer.MT1002), 158);
225 | }
226 |
227 | [Test]
228 | public void do_not_monitor_on_exception()
229 | {
230 | HasDiagnosticAtLine(TestCases._007_LockOnObjectWithWeakIdentity, nameof(LockObjectSelectionAnalyzer.MT1002), 192);
231 | }
232 |
233 | [Test]
234 | public void do_not_monitor_try_on_exception()
235 | {
236 | HasDiagnosticAtLine(TestCases._007_LockOnObjectWithWeakIdentity, nameof(LockObjectSelectionAnalyzer.MT1002), 199);
237 | }
238 |
239 | protected override string LanguageName => LanguageNames.CSharp;
240 |
241 | protected override DiagnosticAnalyzer CreateAnalyzer()
242 | {
243 | return new LockObjectSelectionAnalyzer();
244 | }
245 | }
246 | }
247 |
--------------------------------------------------------------------------------
/src/MultithreadingAnalyzer.Test/LockObjectSelection/TestCases.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace SmartAnalyzers.MultithreadingAnalyzer.Test.LockObjectSelection {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class TestCases {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal TestCases() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SmartAnalyzers.MultithreadingAnalyzer.Test.LockObjectSelection.TestCases", typeof(TestCases).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized string similar to using System.Threading;
65 | ///
66 | ///namespace SmartAnalyzers.MultithreadingAnalyzer.Test.LockObjectSelection.TestData
67 | ///{
68 | /// class _001_LockOnNonReadonlyField
69 | /// {
70 | /// private object lockobj = new object();
71 | ///
72 | /// public void DoSth1()
73 | /// {
74 | /// lock (lockobj)
75 | /// {
76 | /// }
77 | /// }
78 | ///
79 | /// public void DoSth2()
80 | /// {
81 | /// Monitor.Enter(lockobj);
82 | /// Monitor.Exit(lockobj);
83 | /// }
84 | ///
85 | /// public void DoSth3()
86 | /// {
87 | /// var wasTake [rest of string was truncated]";.
88 | ///
89 | internal static string _001_LockOnNonReadonlyField {
90 | get {
91 | return ResourceManager.GetString("_001_LockOnNonReadonlyField", resourceCulture);
92 | }
93 | }
94 |
95 | ///
96 | /// Looks up a localized string similar to using System.Threading;
97 | ///
98 | ///namespace SmartAnalyzers.MultithreadingAnalyzer.Test.LockObjectSelection.TestData
99 | ///{
100 | /// class _002_LockOnNonReadonlyProperty
101 | /// {
102 | /// private object LockObj { get; set; } = new object();
103 | ///
104 | /// public void DoSth1()
105 | /// {
106 | /// lock (LockObj)
107 | /// {
108 | /// }
109 | /// }
110 | ///
111 | /// public void DoSth2()
112 | /// {
113 | /// Monitor.Enter(LockObj);
114 | /// Monitor.Exit(LockObj);
115 | /// }
116 | ///
117 | /// public void DoSth3()
118 | /// {
119 | /// [rest of string was truncated]";.
120 | ///
121 | internal static string _002_LockOnNonReadonlyProperty {
122 | get {
123 | return ResourceManager.GetString("_002_LockOnNonReadonlyProperty", resourceCulture);
124 | }
125 | }
126 |
127 | ///
128 | /// Looks up a localized string similar to using System.Threading;
129 | ///
130 | ///namespace SmartAnalyzers.MultithreadingAnalyzer.Test.LockObjectSelection.TestData
131 | ///{
132 | /// class _003_LockOnPublicField
133 | /// {
134 | /// public object lockobj = new object();
135 | ///
136 | /// public void DoSth1()
137 | /// {
138 | /// lock (lockobj)
139 | /// {
140 | /// }
141 | /// }
142 | ///
143 | /// public void DoSth2()
144 | /// {
145 | /// Monitor.Enter(lockobj);
146 | /// Monitor.Exit(lockobj);
147 | /// }
148 | ///
149 | /// public void DoSth3()
150 | /// {
151 | /// var wasTaken = fa [rest of string was truncated]";.
152 | ///
153 | internal static string _003_LockOnPublicField {
154 | get {
155 | return ResourceManager.GetString("_003_LockOnPublicField", resourceCulture);
156 | }
157 | }
158 |
159 | ///
160 | /// Looks up a localized string similar to using System.Threading;
161 | ///
162 | ///namespace SmartAnalyzers.MultithreadingAnalyzer.Test.LockObjectSelection.TestData
163 | ///{
164 | /// class _004_LockOnPublicProperty
165 | /// {
166 | /// public object LockObj { get; set; } = new object();
167 | ///
168 | /// public void DoSth1()
169 | /// {
170 | /// lock (LockObj)
171 | /// {
172 | /// }
173 | /// }
174 | ///
175 | /// public void DoSth2()
176 | /// {
177 | /// Monitor.Enter(LockObj);
178 | /// Monitor.Exit(LockObj);
179 | /// }
180 | ///
181 | /// public void DoSth3()
182 | /// {
183 | /// [rest of string was truncated]";.
184 | ///
185 | internal static string _004_LockOnPublicProperty {
186 | get {
187 | return ResourceManager.GetString("_004_LockOnPublicProperty", resourceCulture);
188 | }
189 | }
190 |
191 | ///
192 | /// Looks up a localized string similar to using System.Runtime.CompilerServices;
193 | ///using System.Threading;
194 | ///
195 | ///namespace SmartAnalyzers.MultithreadingAnalyzer.Test.LockObjectSelection.TestData
196 | ///{
197 | /// class _005_LockOnThisInstance
198 | /// {
199 | /// public void DoSth1()
200 | /// {
201 | /// Monitor.Enter(this);
202 | /// Monitor.Exit(this);
203 | /// }
204 | ///
205 | /// public void DoSth2()
206 | /// {
207 | /// var wasTaken = false;
208 | /// Monitor.TryEnter(this, ref wasTaken);
209 | /// if (wasTaken)
210 | /// {
211 | /// Monitor.Exi [rest of string was truncated]";.
212 | ///
213 | internal static string _005_LockOnThisInstance {
214 | get {
215 | return ResourceManager.GetString("_005_LockOnThisInstance", resourceCulture);
216 | }
217 | }
218 |
219 | ///
220 | /// Looks up a localized string similar to using System.Threading;
221 | ///
222 | ///namespace SmartAnalyzers.MultithreadingAnalyzer.Test.LockObjectSelection.TestData
223 | ///{
224 | /// struct MyStruct
225 | /// {
226 | /// public int SampleProp { get; set; }
227 | /// }
228 | ///
229 | /// class _006_LockOnValueType
230 | /// {
231 | /// private MyStruct lockObj = new MyStruct();
232 | ///
233 | /// public void DoSth1()
234 | /// {
235 | /// Monitor.Enter(lockObj);
236 | /// Monitor.Exit(lockObj);
237 | /// }
238 | ///
239 | /// public void DoSth2()
240 | /// {
241 | /// var wasTaken = false;
242 | /// Monitor.T [rest of string was truncated]";.
243 | ///
244 | internal static string _006_LockOnValueType {
245 | get {
246 | return ResourceManager.GetString("_006_LockOnValueType", resourceCulture);
247 | }
248 | }
249 |
250 | ///
251 | /// Looks up a localized string similar to using System;
252 | ///using System.Threading;
253 | ///
254 | ///namespace SmartAnalyzers.MultithreadingAnalyzer.Test.LockObjectSelection.TestData
255 | ///{
256 | /// class _007_LockOnObjectWithWeakIdentity
257 | /// {
258 | /// private readonly Type lockType = typeof(_007_LockOnObjectWithWeakIdentity);
259 | ///
260 | /// public void DoSth1()
261 | /// {
262 | /// lock (lockType)
263 | /// {
264 | ///
265 | /// }
266 | /// }
267 | ///
268 | /// public void DoSth2()
269 | /// {
270 | /// lock (typeof(_007_LockOnObjectWithWeakIdentity))
271 | /// {
272 | ///
273 | /// [rest of string was truncated]";.
274 | ///
275 | internal static string _007_LockOnObjectWithWeakIdentity {
276 | get {
277 | return ResourceManager.GetString("_007_LockOnObjectWithWeakIdentity", resourceCulture);
278 | }
279 | }
280 | }
281 | }
282 |
--------------------------------------------------------------------------------