Skip to content

Commit

Permalink
Merge pull request #1115 from AArnott/targetingUpdates
Browse files Browse the repository at this point in the history
Retarget analyzers to the 3.11.0 version
  • Loading branch information
AArnott committed Nov 21, 2022
2 parents a83e88d + 4079b35 commit 1f1a1a6
Show file tree
Hide file tree
Showing 35 changed files with 270 additions and 207 deletions.
4 changes: 2 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>

<MicroBuildVersion>2.0.87</MicroBuildVersion>
<CodeAnalysisVersion>2.10.0</CodeAnalysisVersion>
<CodeAnalysisVersion Condition="'$(IsTestProject)'=='true'">3.11.0</CodeAnalysisVersion>
<CodeAnalysisVersion>3.11.0</CodeAnalysisVersion>
<CodeAnalysisVersion Condition="'$(IsTestProject)'=='true'">4.4.0</CodeAnalysisVersion>
<CodefixTestingVersion>1.1.1</CodefixTestingVersion>
</PropertyGroup>
<ItemGroup>
Expand Down
33 changes: 31 additions & 2 deletions Microsoft.VisualStudio.Threading.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28413.118
# Visual Studio Version 17
VisualStudioVersion = 17.5.33116.440
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Analyzers", "src\Microsoft.VisualStudio.Threading.Analyzers\Microsoft.VisualStudio.Threading.Analyzers.csproj", "{536F3F9A-B457-43B8-BC93-CE1C16959037}"
EndProject
Expand All @@ -12,6 +12,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.editorconfig = .editorconfig
azure-pipelines\build.yml = azure-pipelines\build.yml
Directory.Build.props = Directory.Build.props
Directory.Build.targets = Directory.Build.targets
Directory.Packages.props = Directory.Packages.props
global.json = global.json
nuget.config = nuget.config
stylecop.json = stylecop.json
Expand All @@ -34,6 +36,21 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Thre
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Analyzers.VisualBasic", "src\Microsoft.VisualStudio.Threading.Analyzers.VisualBasic\Microsoft.VisualStudio.Threading.Analyzers.VisualBasic.csproj", "{8CDF7526-D625-4E16-A266-BAF654ABE181}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{96134B19-FB32-4FA0-A565-BD4247D1E5B2}"
ProjectSection(SolutionItems) = preProject
src\.editorconfig = src\.editorconfig
src\AssemblyInfo.cs = src\AssemblyInfo.cs
src\Directory.Build.props = src\Directory.Build.props
src\Directory.Build.targets = src\Directory.Build.targets
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{C1DAF484-769B-4C5E-9DD1-A4CFAA66E938}"
ProjectSection(SolutionItems) = preProject
test\.editorconfig = test\.editorconfig
test\Directory.Build.props = test\Directory.Build.props
test\Directory.Build.targets = test\Directory.Build.targets
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -124,6 +141,18 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{536F3F9A-B457-43B8-BC93-CE1C16959037} = {96134B19-FB32-4FA0-A565-BD4247D1E5B2}
{620ED702-B6DA-4454-BF3E-5494D3652724} = {C1DAF484-769B-4C5E-9DD1-A4CFAA66E938}
{4961AA84-088C-46C0-BAC0-F9E87A9F03A7} = {C1DAF484-769B-4C5E-9DD1-A4CFAA66E938}
{D9BB9FB6-3833-44E8-B7A7-DE729FCE214D} = {96134B19-FB32-4FA0-A565-BD4247D1E5B2}
{CBEDB102-ABAE-40B1-AF3F-A6226DB6713D} = {C1DAF484-769B-4C5E-9DD1-A4CFAA66E938}
{BA4643D8-E6B2-4DED-882F-4827F3AB6AB0} = {C1DAF484-769B-4C5E-9DD1-A4CFAA66E938}
{3BDB8F46-A39C-422B-8B0E-89E98B83073F} = {96134B19-FB32-4FA0-A565-BD4247D1E5B2}
{7177DEEE-D14D-4A4A-BF6E-8B0CDC26B624} = {96134B19-FB32-4FA0-A565-BD4247D1E5B2}
{D5A0D627-7853-43F5-9AF4-E23D062C6ABA} = {96134B19-FB32-4FA0-A565-BD4247D1E5B2}
{8CDF7526-D625-4E16-A266-BAF654ABE181} = {96134B19-FB32-4FA0-A565-BD4247D1E5B2}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E2124DFF-970E-4BA1-9E50-3ADB0AABF347}
EndGlobalSection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ internal static bool ShouldIgnoreContext(SyntaxNodeAnalysisContext context)
}
}

private static SyntaxNode? GetEnclosingBlock(SyntaxNode node)
private static SyntaxNode? GetEnclosingBlock(SyntaxNode? node)
{
while (node is not null)
{
Expand Down Expand Up @@ -186,7 +186,7 @@ private static bool IsTaskCompletedWithWhenAll(SyntaxNodeAnalysisContext context

// Is this `Task.WhenAll` invocation from the System.Threading.Tasks.Task type?
ITypeSymbol? classType = context.SemanticModel.GetTypeInfo(memberAccess.Expression).Type;
var correctType = classType.Name == Types.Task.TypeName && classType.BelongsToNamespace(Types.Task.Namespace);
var correctType = classType?.Name == Types.Task.TypeName && classType.BelongsToNamespace(Types.Task.Namespace);
if (!correctType)
{
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ internal static ExpressionSyntax IsolateMethodName(InvocationExpressionSyntax in
/// </summary>
/// <param name="syntaxNode">The syntax node to begin the search from.</param>
/// <returns>The containing function, and metadata for it.</returns>
internal static ContainingFunctionData GetContainingFunction(CSharpSyntaxNode syntaxNode)
internal static ContainingFunctionData GetContainingFunction(CSharpSyntaxNode? syntaxNode)
{
while (syntaxNode is object)
{
Expand Down Expand Up @@ -103,7 +103,7 @@ internal static ContainingFunctionData GetContainingFunction(CSharpSyntaxNode sy
return new ContainingFunctionData(method, method.Modifiers.Any(SyntaxKind.AsyncKeyword), method.ParameterList, method.Body, bodyReplacement);
}

syntaxNode = (CSharpSyntaxNode)syntaxNode.Parent;
syntaxNode = (CSharpSyntaxNode?)syntaxNode.Parent;
}

return default(ContainingFunctionData);
Expand Down Expand Up @@ -147,7 +147,7 @@ internal static bool IsAssignedWithin(SyntaxNode container, SemanticModel semant
if (node is AssignmentExpressionSyntax assignment)
{
ISymbol? assignedSymbol = semanticModel.GetSymbolInfo(assignment.Left, cancellationToken).Symbol;
if (variable.Equals(assignedSymbol))
if (variable.Equals(assignedSymbol, SymbolEqualityComparer.Default))
{
return true;
}
Expand Down Expand Up @@ -207,7 +207,7 @@ internal static bool IsWithinNameOf([NotNullWhen(true)] SyntaxNode? syntaxNode)
foreach (BaseTypeSyntax? baseTypeSyntax in typeDeclarationSyntax.BaseList.Types)
{
SymbolInfo baseTypeSymbolInfo = semanticModel.GetSymbolInfo(baseTypeSyntax.Type, cancellationToken);
if (Equals(baseTypeSymbolInfo.Symbol, baseType))
if (SymbolEqualityComparer.Default.Equals(baseTypeSymbolInfo.Symbol, baseType))
{
return baseTypeSyntax.GetLocation();
}
Expand Down Expand Up @@ -264,7 +264,7 @@ internal override bool MethodReturnsNullableReferenceType(IMethodSymbol methodSy

internal readonly struct ContainingFunctionData
{
internal ContainingFunctionData(CSharpSyntaxNode function, bool isAsync, ParameterListSyntax parameterList, CSharpSyntaxNode blockOrExpression, Func<CSharpSyntaxNode, CSharpSyntaxNode> bodyReplacement)
internal ContainingFunctionData(CSharpSyntaxNode function, bool isAsync, ParameterListSyntax? parameterList, CSharpSyntaxNode? blockOrExpression, Func<CSharpSyntaxNode, CSharpSyntaxNode> bodyReplacement)
{
this.Function = function;
this.IsAsync = isAsync;
Expand All @@ -277,9 +277,9 @@ internal ContainingFunctionData(CSharpSyntaxNode function, bool isAsync, Paramet

internal bool IsAsync { get; }

internal ParameterListSyntax ParameterList { get; }
internal ParameterListSyntax? ParameterList { get; }

internal CSharpSyntaxNode BlockOrExpression { get; }
internal CSharpSyntaxNode? BlockOrExpression { get; }

internal Func<CSharpSyntaxNode, CSharpSyntaxNode> BodyReplacement { get; }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>Microsoft.VisualStudio.Threading.Analyzers</RootNamespace>

<IsPackable>false</IsPackable>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,9 @@ public override void Initialize(AnalysisContext context)
if (firstParameter is object)
{
// Are we accessing a member of the completed task?
ISymbol invokedObjectSymbol = context.SemanticModel.GetSymbolInfo(memberAccessSyntax.Expression, context.CancellationToken).Symbol;
IParameterSymbol completedTask = context.SemanticModel.GetDeclaredSymbol(firstParameter);
if (EqualityComparer<ISymbol>.Default.Equals(invokedObjectSymbol, completedTask))
ISymbol? invokedObjectSymbol = context.SemanticModel.GetSymbolInfo(memberAccessSyntax.Expression, context.CancellationToken).Symbol;
IParameterSymbol? completedTask = context.SemanticModel.GetDeclaredSymbol(firstParameter);
if (EqualityComparer<ISymbol?>.Default.Equals(invokedObjectSymbol, completedTask))
{
// Skip analysis since Task.Result (et. al) of a completed Task is fair game.
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ private void AnalyzeAwaitExpression(SyntaxNodeAnalysisContext context)
}
}

private Diagnostic? AnalyzeAwaitedOrReturnedExpression(ExpressionSyntax expressionSyntax, SyntaxNodeAnalysisContext context, CancellationToken cancellationToken)
private Diagnostic? AnalyzeAwaitedOrReturnedExpression(ExpressionSyntax? expressionSyntax, SyntaxNodeAnalysisContext context, CancellationToken cancellationToken)
{
if (expressionSyntax is null)
{
Expand Down Expand Up @@ -242,7 +242,7 @@ private void AnalyzeAwaitExpression(SyntaxNodeAnalysisContext context)
// Consider the implicit first argument when this method is invoked as an extension method.
if (methodSymbol.IsExtensionMethod && invocationExpressionSyntax.Expression is MemberAccessExpressionSyntax invokedMember)
{
if (!methodSymbol.ContainingType.Equals(semanticModel.GetSymbolInfo(invokedMember.Expression, cancellationToken).Symbol))
if (!methodSymbol.ContainingType.Equals(semanticModel.GetSymbolInfo(invokedMember.Expression, cancellationToken).Symbol, SymbolEqualityComparer.Default))
{
expressionsToConsider = new ExpressionSyntax[] { invokedMember.Expression }.Concat(expressionsToConsider);
}
Expand All @@ -268,11 +268,11 @@ private void AnalyzeAwaitExpression(SyntaxNodeAnalysisContext context)
if (dataflowAnalysisCompatibleVariable)
{
// Run data flow analysis to understand where the task was defined
DataFlowAnalysis dataFlowAnalysis;
DataFlowAnalysis? dataFlowAnalysis;

// When possible (await is direct child of the block and not a field), execute data flow analysis by passing first and last statement to capture only what happens before the await
// Check if the await is direct child of the code block (first parent is ExpressionStantement, second parent is the block itself)
if (delegateBlock.Equals(expressionSyntax.Parent.Parent?.Parent))
if (delegateBlock.Equals(expressionSyntax.Parent?.Parent?.Parent))
{
dataFlowAnalysis = semanticModel.AnalyzeDataFlow(delegateBlock.ChildNodes().First(), expressionSyntax.Parent.Parent);
}
Expand All @@ -282,7 +282,7 @@ private void AnalyzeAwaitExpression(SyntaxNodeAnalysisContext context)
dataFlowAnalysis = semanticModel.AnalyzeDataFlow(delegateBlock);
}

if (!dataFlowAnalysis.WrittenInside.Contains(symbolToConsider.Symbol))
if (dataFlowAnalysis?.WrittenInside.Contains(symbolToConsider.Symbol) is false)
{
return Diagnostic.Create(Descriptor, expressionSyntax.GetLocation());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ public class VSTHRD010MainThreadUsageAnalyzer : DiagnosticAnalyzer
helpLinkUri: Utils.GetHelpLink(Id),
category: "Usage",
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);
isEnabledByDefault: true,
customTags: WellKnownDiagnosticTags.CompilationEnd);

/// <summary>
/// The descriptor to use for diagnostics reported in async methods.
Expand All @@ -76,7 +77,8 @@ public class VSTHRD010MainThreadUsageAnalyzer : DiagnosticAnalyzer
helpLinkUri: Utils.GetHelpLink(Id),
category: "Usage",
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);
isEnabledByDefault: true,
customTags: WellKnownDiagnosticTags.CompilationEnd);

/// <summary>
/// A reusable value to return from <see cref="SupportedDiagnostics"/>.
Expand Down Expand Up @@ -120,13 +122,13 @@ public override void Initialize(AnalysisContext context)
var mainThreadAssertingMethods = CommonInterest.ReadMethods(compilationStartContext.Options, CommonInterest.FileNamePatternForMethodsThatAssertMainThread, compilationStartContext.CancellationToken).ToImmutableArray();
var mainThreadSwitchingMethods = CommonInterest.ReadMethods(compilationStartContext.Options, CommonInterest.FileNamePatternForMethodsThatSwitchToMainThread, compilationStartContext.CancellationToken).ToImmutableArray();
var membersRequiringMainThread = CommonInterest.ReadTypesAndMembers(compilationStartContext.Options, CommonInterest.FileNamePatternForMembersRequiringMainThread, compilationStartContext.CancellationToken).ToImmutableArray();
ImmutableDictionary<string, string>? diagnosticProperties = ImmutableDictionary<string, string>.Empty
ImmutableDictionary<string, string?>? diagnosticProperties = ImmutableDictionary<string, string?>.Empty
.Add(CommonInterest.FileNamePatternForMethodsThatAssertMainThread.ToString(), string.Join("\n", mainThreadAssertingMethods))
.Add(CommonInterest.FileNamePatternForMethodsThatSwitchToMainThread.ToString(), string.Join("\n", mainThreadSwitchingMethods));
var methodsDeclaringUIThreadRequirement = new HashSet<IMethodSymbol>();
var methodsAssertingUIThreadRequirement = new HashSet<IMethodSymbol>();
var callerToCalleeMap = new Dictionary<IMethodSymbol, List<CallInfo>>();
var methodsDeclaringUIThreadRequirement = new HashSet<IMethodSymbol>(SymbolEqualityComparer.Default);
var methodsAssertingUIThreadRequirement = new HashSet<IMethodSymbol>(SymbolEqualityComparer.Default);
var callerToCalleeMap = new Dictionary<IMethodSymbol, List<CallInfo>>(SymbolEqualityComparer.Default);
compilationStartContext.RegisterCodeBlockStartAction<SyntaxKind>(codeBlockStartContext =>
{
Expand Down Expand Up @@ -157,10 +159,10 @@ public override void Initialize(AnalysisContext context)
HashSet<IMethodSymbol>? transitiveClosureOfMainThreadRequiringMethods = GetTransitiveClosureOfMainThreadRequiringMethods(methodsAssertingUIThreadRequirement, calleeToCallerMap);
foreach (IMethodSymbol? implicitUserMethod in transitiveClosureOfMainThreadRequiringMethods.Except(methodsDeclaringUIThreadRequirement))
{
var reportSites = from info in callerToCalleeMap[implicitUserMethod]
where transitiveClosureOfMainThreadRequiringMethods.Contains(info.MethodSymbol)
group info by info.MethodSymbol into bySymbol
select new { Location = bySymbol.First().InvocationSyntax.GetLocation(), CalleeMethod = bySymbol.Key };
var reportSites = callerToCalleeMap[implicitUserMethod]
.Where(info => transitiveClosureOfMainThreadRequiringMethods.Contains(info.MethodSymbol))
.GroupBy<CallInfo, ISymbol>(info => info.MethodSymbol, SymbolEqualityComparer.Default)
.Select(bySymbol => new { Location = bySymbol.First().InvocationSyntax.GetLocation(), CalleeMethod = bySymbol.Key });
foreach (var site in reportSites)
{
bool isAsync = Utils.IsAsyncReady(implicitUserMethod);
Expand All @@ -181,7 +183,7 @@ where transitiveClosureOfMainThreadRequiringMethods.Contains(info.MethodSymbol)

private static HashSet<IMethodSymbol> GetTransitiveClosureOfMainThreadRequiringMethods(HashSet<IMethodSymbol> methodsRequiringUIThread, Dictionary<IMethodSymbol, List<CallInfo>> calleeToCallerMap)
{
var result = new HashSet<IMethodSymbol>();
var result = new HashSet<IMethodSymbol>(SymbolEqualityComparer.Default);

void MarkMethod(IMethodSymbol method)
{
Expand All @@ -208,7 +210,7 @@ void MarkMethod(IMethodSymbol method)

private static Dictionary<IMethodSymbol, List<CallInfo>> CreateCalleeToCallerMap(Dictionary<IMethodSymbol, List<CallInfo>> callerToCalleeMap)
{
var result = new Dictionary<IMethodSymbol, List<CallInfo>>();
var result = new Dictionary<IMethodSymbol, List<CallInfo>>(SymbolEqualityComparer.Default);

foreach (KeyValuePair<IMethodSymbol, List<CallInfo>> item in callerToCalleeMap)
{
Expand Down Expand Up @@ -307,7 +309,7 @@ private class MethodAnalyzer
ImmutableArray<CommonInterest.TypeMatchSpec> membersRequiringMainThread,
HashSet<IMethodSymbol> methodsDeclaringUIThreadRequirement,
HashSet<IMethodSymbol> methodsAssertingUIThreadRequirement,
ImmutableDictionary<string, string> diagnosticProperties)
ImmutableDictionary<string, string?> diagnosticProperties)
{
this.MainThreadAssertingMethods = mainThreadAssertingMethods;
this.MainThreadSwitchingMethods = mainThreadSwitchingMethods;
Expand All @@ -327,7 +329,7 @@ private class MethodAnalyzer

internal HashSet<IMethodSymbol> MethodsAssertingUIThreadRequirement { get; }

internal ImmutableDictionary<string, string> DiagnosticProperties { get; }
internal ImmutableDictionary<string, string?> DiagnosticProperties { get; }

internal void AnalyzeInvocation(SyntaxNodeAnalysisContext context)
{
Expand Down Expand Up @@ -452,10 +454,10 @@ private static bool IsObjectLikelyToBeCOMObject(ITypeSymbol typeSymbol)
}

return typeSymbol.GetAttributes().Any(ad =>
(ad.AttributeClass.Name == Types.CoClassAttribute.TypeName && ad.AttributeClass.BelongsToNamespace(Types.CoClassAttribute.Namespace)) ||
(ad.AttributeClass.Name == Types.ComImportAttribute.TypeName && ad.AttributeClass.BelongsToNamespace(Types.ComImportAttribute.Namespace)) ||
(ad.AttributeClass.Name == Types.InterfaceTypeAttribute.TypeName && ad.AttributeClass.BelongsToNamespace(Types.InterfaceTypeAttribute.Namespace)) ||
(ad.AttributeClass.Name == Types.TypeLibTypeAttribute.TypeName && ad.AttributeClass.BelongsToNamespace(Types.TypeLibTypeAttribute.Namespace)));
(ad.AttributeClass?.Name == Types.CoClassAttribute.TypeName && ad.AttributeClass.BelongsToNamespace(Types.CoClassAttribute.Namespace)) ||
(ad.AttributeClass?.Name == Types.ComImportAttribute.TypeName && ad.AttributeClass.BelongsToNamespace(Types.ComImportAttribute.Namespace)) ||
(ad.AttributeClass?.Name == Types.InterfaceTypeAttribute.TypeName && ad.AttributeClass.BelongsToNamespace(Types.InterfaceTypeAttribute.Namespace)) ||
(ad.AttributeClass?.Name == Types.TypeLibTypeAttribute.TypeName && ad.AttributeClass.BelongsToNamespace(Types.TypeLibTypeAttribute.Namespace)));
}

private bool AnalyzeMemberWithinContext(ITypeSymbol type, ISymbol? symbol, SyntaxNodeAnalysisContext context, Location? focusDiagnosticOn = null)
Expand Down
Loading

0 comments on commit 1f1a1a6

Please sign in to comment.