Merge pull request #339 from sharwell/customize-legacy-apis

Allow extensions to the set of legacy thread switching members
This commit is contained in:
Andrew Arnott 2018-08-07 09:35:30 -07:00 коммит произвёл GitHub
Родитель 7838d5a9b0 5b77a4064f
Коммит ad33237fbe
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 57 добавлений и 32 удалений

Просмотреть файл

@ -26,3 +26,8 @@ void Foo() {
});
}
```
## Configuration
This analyzer is configurable via the `vs-threading.LegacyThreadSwitchingMembers.txt` file.
See our [configuration](configuration.md) topic for more information.

Просмотреть файл

@ -66,3 +66,16 @@ These are identified as described below:
Properties are specified by their name, not the name of their accessors.
For example, a property should be specified by `PropertyName`, not `get_PropertyName`.
## Legacy thread switching members
Prior to Microsoft.VisualStudio.Threading, libraries provided additional ways to execute
code on the UI thread. These methods should be avoided, and code should update to using
`JoinableTaskFactory.SwitchToMainThreadAsync()` as the standard way to switch to the main
thread.
**Filename:** `vs-threading.LegacyThreadSwitchingMembers.txt`
**Line format:** `[Namespace.TypeName]::MethodName`
**Sample:** `[System.Windows.Threading.Dispatcher]::Invoke`

Просмотреть файл

@ -18,6 +18,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
internal static class CommonInterest
{
internal static readonly Regex FileNamePatternForLegacyThreadSwitchingMembers = new Regex(@"^vs-threading\.LegacyThreadSwitchingMembers(\..*)?.txt$", FileNamePatternRegexOptions);
internal static readonly Regex FileNamePatternForMembersRequiringMainThread = new Regex(@"^vs-threading\.MembersRequiringMainThread(\..*)?.txt$", FileNamePatternRegexOptions);
internal static readonly Regex FileNamePatternForMethodsThatAssertMainThread = new Regex(@"^vs-threading\.MainThreadAssertingMethods(\..*)?.txt$", FileNamePatternRegexOptions);
internal static readonly Regex FileNamePatternForMethodsThatSwitchToMainThread = new Regex(@"^vs-threading\.MainThreadSwitchingMethods(\..*)?.txt$", FileNamePatternRegexOptions);
@ -40,18 +41,6 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
new SyncBlockingMethod(new QualifiedMember(new QualifiedType(Namespaces.MicrosoftVisualStudioShellInterop, "IVsTask"), "GetResult"), extensionMethodNamespace: Namespaces.MicrosoftVisualStudioShell),
});
internal static readonly IEnumerable<QualifiedMember> LegacyThreadSwitchingMethods = new[]
{
new QualifiedMember(new QualifiedType(Namespaces.MicrosoftVisualStudioShell, Types.ThreadHelper.TypeName), Types.ThreadHelper.Invoke),
new QualifiedMember(new QualifiedType(Namespaces.MicrosoftVisualStudioShell, Types.ThreadHelper.TypeName), Types.ThreadHelper.InvokeAsync),
new QualifiedMember(new QualifiedType(Namespaces.MicrosoftVisualStudioShell, Types.ThreadHelper.TypeName), Types.ThreadHelper.BeginInvoke),
new QualifiedMember(new QualifiedType(Namespaces.SystemWindowsThreading, Types.Dispatcher.TypeName), Types.Dispatcher.Invoke),
new QualifiedMember(new QualifiedType(Namespaces.SystemWindowsThreading, Types.Dispatcher.TypeName), Types.Dispatcher.BeginInvoke),
new QualifiedMember(new QualifiedType(Namespaces.SystemWindowsThreading, Types.Dispatcher.TypeName), Types.Dispatcher.InvokeAsync),
new QualifiedMember(new QualifiedType(Namespaces.SystemThreading, Types.SynchronizationContext.TypeName), Types.SynchronizationContext.Send),
new QualifiedMember(new QualifiedType(Namespaces.SystemThreading, Types.SynchronizationContext.TypeName), Types.SynchronizationContext.Post),
};
internal static readonly IReadOnlyList<SyncBlockingMethod> SyncBlockingProperties = new[]
{
new SyncBlockingMethod(new QualifiedMember(new QualifiedType(Namespaces.SystemThreadingTasks, nameof(Task)), nameof(Task<int>.Result)), null),

Просмотреть файл

@ -1,11 +1,6 @@
namespace Microsoft.VisualStudio.Threading.Analyzers
{
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CodeAnalysis;
using CodeAnalysis.CSharp;
using CodeAnalysis.CSharp.Syntax;
@ -32,26 +27,41 @@
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze);
context.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(this.AnalyzeInvocation), SyntaxKind.InvocationExpression);
context.RegisterCompilationStartAction(compilationStartContext =>
{
var legacyThreadSwitchingMembers = CommonInterest.ReadMethods(compilationStartContext.Options, CommonInterest.FileNamePatternForLegacyThreadSwitchingMembers, compilationStartContext.CancellationToken).ToImmutableArray();
var analyzer = new Analyzer(legacyThreadSwitchingMembers);
compilationStartContext.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(analyzer.AnalyzeInvocation), SyntaxKind.InvocationExpression);
});
}
private void AnalyzeInvocation(SyntaxNodeAnalysisContext context)
private class Analyzer
{
var invocationSyntax = (InvocationExpressionSyntax)context.Node;
var invokeMethod = context.SemanticModel.GetSymbolInfo(context.Node).Symbol as IMethodSymbol;
if (invokeMethod != null)
{
foreach (var legacyMethod in CommonInterest.LegacyThreadSwitchingMethods)
{
context.CancellationToken.ThrowIfCancellationRequested();
private readonly ImmutableArray<CommonInterest.QualifiedMember> legacyThreadSwitchingMembers;
if (legacyMethod.IsMatch(invokeMethod))
public Analyzer(ImmutableArray<CommonInterest.QualifiedMember> legacyThreadSwitchingMembers)
{
this.legacyThreadSwitchingMembers = legacyThreadSwitchingMembers;
}
internal void AnalyzeInvocation(SyntaxNodeAnalysisContext context)
{
var invocationSyntax = (InvocationExpressionSyntax)context.Node;
var invokeMethod = context.SemanticModel.GetSymbolInfo(context.Node).Symbol as IMethodSymbol;
if (invokeMethod != null)
{
foreach (var legacyMethod in this.legacyThreadSwitchingMembers)
{
var diagnostic = Diagnostic.Create(
Descriptor,
invocationSyntax.Expression.GetLocation());
context.ReportDiagnostic(diagnostic);
break;
context.CancellationToken.ThrowIfCancellationRequested();
if (legacyMethod.IsMatch(invokeMethod))
{
var diagnostic = Diagnostic.Create(
Descriptor,
invocationSyntax.Expression.GetLocation());
context.ReportDiagnostic(diagnostic);
break;
}
}
}
}

Просмотреть файл

@ -0,0 +1,8 @@
[Microsoft.VisualStudio.Shell.ThreadHelper]::Invoke
[Microsoft.VisualStudio.Shell.ThreadHelper]::InvokeAsync
[Microsoft.VisualStudio.Shell.ThreadHelper]::BeginInvoke
[System.Windows.Threading.Dispatcher]::Invoke
[System.Windows.Threading.Dispatcher]::InvokeAsync
[System.Windows.Threading.Dispatcher]::BeginInvoke
[System.Threading.SynchronizationContext]::Send
[System.Threading.SynchronizationContext]::Post