Apply latest Library.Template pattern

Also enable some analyzers and fix up all the warnings.
This commit is contained in:
Andrew Arnott 2020-09-14 18:21:23 -06:00
Родитель 7437af0c86
Коммит 4de5495c75
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: A9B9910CDCCDA441
291 изменённых файлов: 5431 добавлений и 5071 удалений

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

@ -1,48 +1,125 @@
# EditorConfig is awesome:http://EditorConfig.org
# top-most EditorConfig file
root = true
# Don't use tabs for indentation.
[*]
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
# (Please don't specify an indent_size here; that has too many unintended consequences.)
[*.yml]
indent_size = 2
indent_style = space
[*.{xml,targets,props}]
# Code files
[*.{cs,csx,vb,vbx,h,cpp,idl}]
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
# Xml project files
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj,runsettings}]
indent_size = 2
[*.cs]
indent_style = space
indent_size = 4
# Xml config files
[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
indent_size = 2
# Make Visual Studio consistent with SA1101 (use "this" prefix), but allow StyleCop to report it
dotnet_style_qualification_for_field = true:silent
dotnet_style_qualification_for_property = true:silent
dotnet_style_qualification_for_method = true:silent
dotnet_style_qualification_for_event = true:silent
# JSON files
[*.json]
indent_size = 2
# Make Visual Studio consistent with SA1121 (use predefined type), but allow StyleCop to report it
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
dotnet_style_predefined_type_for_member_access = true:silent
csharp_style_var_for_built_in_types = false:silent
# Additional settings to make the Visual Studio formatter follow StyleCop
# Dotnet code style settings:
[*.{cs,vb}]
# Sort using and Import directives with System.* appearing first
dotnet_sort_system_directives_first = true
dotnet_style_qualification_for_field = true:warning
dotnet_style_qualification_for_property = true:warning
dotnet_style_qualification_for_method = true:warning
dotnet_style_qualification_for_event = true:warning
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = false
# Use language keywords instead of framework type names for type references
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
# New line preferences
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_within_query_expression_clauses = true
# Suggest more modern language features when available
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
# Non-private static fields are PascalCase
dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non_private_static_fields
dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style
dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field
dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected internal, private protected
dotnet_naming_symbols.non_private_static_fields.required_modifiers = static
dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case
# Constants are PascalCase
dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants
dotnet_naming_rule.constants_should_be_pascal_case.style = constant_style
dotnet_naming_symbols.constants.applicable_kinds = field, local
dotnet_naming_symbols.constants.required_modifiers = const
dotnet_naming_style.constant_style.capitalization = pascal_case
# Static fields are camelCase
dotnet_naming_rule.static_fields_should_be_camel_case.severity = suggestion
dotnet_naming_rule.static_fields_should_be_camel_case.symbols = static_fields
dotnet_naming_rule.static_fields_should_be_camel_case.style = static_field_style
dotnet_naming_symbols.static_fields.applicable_kinds = field
dotnet_naming_symbols.static_fields.required_modifiers = static
dotnet_naming_style.static_field_style.capitalization = camel_case
# Instance fields are camelCase
dotnet_naming_rule.instance_fields_should_be_camel_case.severity = suggestion
dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields
dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style
dotnet_naming_symbols.instance_fields.applicable_kinds = field
dotnet_naming_style.instance_field_style.capitalization = camel_case
# Locals and parameters are camelCase
dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion
dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters
dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style
dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local
dotnet_naming_style.camel_case_style.capitalization = camel_case
# Local functions are PascalCase
dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions
dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style
dotnet_naming_symbols.local_functions.applicable_kinds = local_function
dotnet_naming_style.local_function_style.capitalization = pascal_case
# By default, name items with PascalCase
dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members
dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style
dotnet_naming_symbols.all_members.applicable_kinds = *
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
# CSharp code style settings:
[*.cs]
# Indentation preferences
csharp_indent_block_contents = true
csharp_indent_braces = false
@ -50,29 +127,64 @@ csharp_indent_case_contents = true
csharp_indent_switch_labels = true
csharp_indent_labels = flush_left
# Space preferences
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = do_not_ignore
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
# Prefer "var" everywhere
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_elsewhere = false:warning
# RS2008: Enable analyzer release tracking
dotnet_diagnostic.RS2008.severity = none
# Prefer method-like constructs to have a block body
csharp_style_expression_bodied_methods = false:none
csharp_style_expression_bodied_constructors = false:none
csharp_style_expression_bodied_operators = false:none
# Prefer property-like constructs to have an expression-body
csharp_style_expression_bodied_properties = true:none
csharp_style_expression_bodied_indexers = true:none
csharp_style_expression_bodied_accessors = true:none
# Suggest more modern language features when available
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:suggestion
# Newline settings
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
# Blocks are allowed
csharp_prefer_braces = true:silent
[*.cs]
# SA1130: Use lambda syntax
dotnet_diagnostic.SA1130.severity = silent
# SA1404: Code analysis suppression should have justification
dotnet_diagnostic.SA1404.severity = suggestion
# SA1600: Elements should be documented
dotnet_diagnostic.SA1600.severity = none
# SA1611: Element parameters should be documented
dotnet_diagnostic.SA1611.severity = none
# SA1615: Element return value should be documented
dotnet_diagnostic.SA1615.severity = none
# SA1601: Partial elements should be documented
dotnet_diagnostic.SA1601.severity = none
# SA1133: Do not combine attributes
dotnet_diagnostic.SA1133.severity = none
# SA1108: Block statements should not contain embedded comments
dotnet_diagnostic.SA1108.severity = suggestion
[*.sln]
indent_style = tab

50
Directory.Build.props Normal file
Просмотреть файл

@ -0,0 +1,50 @@
<Project>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<RepoRootPath>$(MSBuildThisFileDirectory)</RepoRootPath>
<BaseIntermediateOutputPath>$(RepoRootPath)obj\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
<BaseOutputPath Condition=" '$(BaseOutputPath)' == '' ">$(RepoRootPath)bin\$(MSBuildProjectName)\</BaseOutputPath>
<PackageOutputPath>$(RepoRootPath)bin\Packages\$(Configuration)\NuGet\</PackageOutputPath>
<LangVersion>8.0</LangVersion>
<Nullable>enable</Nullable>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Company>Microsoft</Company>
<Authors>Microsoft</Authors>
<Copyright>© Microsoft. All rights reserved.</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/Microsoft/vs-threading</PackageProjectUrl>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<MicroBuildVersion>2.0.61</MicroBuildVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MicroBuild.VisualStudio" Version="$(MicroBuildVersion)" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.3.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="all" />
<PackageReference Include="Nerdbank.GitVersioning" Version="3.2.31" PrivateAssets="all" />
<PackageReference Include="Microsoft.Net.Compilers.Toolset" Version="3.7.0" PrivateAssets="all" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.164" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="$(MSBuildThisFileDirectory)stylecop.json" Link="stylecop.json" />
</ItemGroup>
<ItemDefinitionGroup>
<!-- We always want MSBuild properties generated that point at the restored location of each package. -->
<PackageReference GeneratePathProperty="true" />
</ItemDefinitionGroup>
<Target Name="PrepareReleaseNotes" BeforeTargets="GenerateNuspec" DependsOnTargets="GetBuildVersion">
<PropertyGroup>
<PackageReleaseNotes>https://github.com/microsoft/vs-threading/releases/tag/v$(Version)</PackageReleaseNotes>
</PropertyGroup>
</Target>
</Project>

2
Directory.Build.targets Normal file
Просмотреть файл

@ -0,0 +1,2 @@
<Project>
</Project>

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

@ -3,37 +3,36 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28413.118
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Analyzers", "Microsoft.VisualStudio.Threading.Analyzers\Microsoft.VisualStudio.Threading.Analyzers.csproj", "{536F3F9A-B457-43B8-BC93-CE1C16959037}"
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
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Analyzers.Tests", "Microsoft.VisualStudio.Threading.Analyzers.Tests\Microsoft.VisualStudio.Threading.Analyzers.Tests.csproj", "{620ED702-B6DA-4454-BF3E-5494D3652724}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Analyzers.Tests", "test\Microsoft.VisualStudio.Threading.Analyzers.Tests\Microsoft.VisualStudio.Threading.Analyzers.Tests.csproj", "{620ED702-B6DA-4454-BF3E-5494D3652724}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{AA091F9F-B40A-466F-B09F-22EAD115996F}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
..\.vsts-ci.yml = ..\.vsts-ci.yml
..\azure-pipelines\build.yml = ..\azure-pipelines\build.yml
azure-pipelines\build.yml = azure-pipelines\build.yml
Directory.Build.props = Directory.Build.props
..\global.json = ..\global.json
..\nuget.config = ..\nuget.config
global.json = global.json
nuget.config = nuget.config
stylecop.json = stylecop.json
version.json = version.json
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher", "Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher\Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher.csproj", "{4961AA84-088C-46C0-BAC0-F9E87A9F03A7}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher", "test\Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher\Microsoft.VisualStudio.Threading.Tests.Win7RegistryWatcher.csproj", "{4961AA84-088C-46C0-BAC0-F9E87A9F03A7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading", "Microsoft.VisualStudio.Threading\Microsoft.VisualStudio.Threading.csproj", "{D9BB9FB6-3833-44E8-B7A7-DE729FCE214D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading", "src\Microsoft.VisualStudio.Threading\Microsoft.VisualStudio.Threading.csproj", "{D9BB9FB6-3833-44E8-B7A7-DE729FCE214D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Tests", "Microsoft.VisualStudio.Threading.Tests\Microsoft.VisualStudio.Threading.Tests.csproj", "{CBEDB102-ABAE-40B1-AF3F-A6226DB6713D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Tests", "test\Microsoft.VisualStudio.Threading.Tests\Microsoft.VisualStudio.Threading.Tests.csproj", "{CBEDB102-ABAE-40B1-AF3F-A6226DB6713D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IsolatedTestHost", "IsolatedTestHost\IsolatedTestHost.csproj", "{BA4643D8-E6B2-4DED-882F-4827F3AB6AB0}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IsolatedTestHost", "test\IsolatedTestHost\IsolatedTestHost.csproj", "{BA4643D8-E6B2-4DED-882F-4827F3AB6AB0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Analyzers.CodeFixes", "Microsoft.VisualStudio.Threading.Analyzers.CodeFixes\Microsoft.VisualStudio.Threading.Analyzers.CodeFixes.csproj", "{3BDB8F46-A39C-422B-8B0E-89E98B83073F}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Analyzers.CodeFixes", "src\Microsoft.VisualStudio.Threading.Analyzers.CodeFixes\Microsoft.VisualStudio.Threading.Analyzers.CodeFixes.csproj", "{3BDB8F46-A39C-422B-8B0E-89E98B83073F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SosThreadingTools", "SosThreadingTools\SosThreadingTools.csproj", "{7177DEEE-D14D-4A4A-BF6E-8B0CDC26B624}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SosThreadingTools", "src\SosThreadingTools\SosThreadingTools.csproj", "{7177DEEE-D14D-4A4A-BF6E-8B0CDC26B624}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Analyzers.CSharp", "Microsoft.VisualStudio.Threading.Analyzers.CSharp\Microsoft.VisualStudio.Threading.Analyzers.CSharp.csproj", "{D5A0D627-7853-43F5-9AF4-E23D062C6ABA}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Analyzers.CSharp", "src\Microsoft.VisualStudio.Threading.Analyzers.CSharp\Microsoft.VisualStudio.Threading.Analyzers.CSharp.csproj", "{D5A0D627-7853-43F5-9AF4-E23D062C6ABA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.Threading.Analyzers.VisualBasic", "Microsoft.VisualStudio.Threading.Analyzers.VisualBasic\Microsoft.VisualStudio.Threading.Analyzers.VisualBasic.csproj", "{8CDF7526-D625-4E16-A266-BAF654ABE181}"
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
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution

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

@ -2,21 +2,23 @@ trigger:
branches:
include:
- master
- 'v16.*'
- microbuild
- 'validate/*'
paths:
exclude: [".github", "doc", "*.md"]
exclude:
- doc/
- '*.md'
- .vscode/
- .github/
variables:
TreatWarningsAsErrors: true
UpdateXlfOnBuild: false # force build breaks if xlf files aren't updated on dev box with resx changes
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
BuildConfiguration: Release
BuildPlatform: Any CPU
# codecov_token: 4dc9e7e2-6b01-4932-a180-847b52b43d35 # Get a new one from https://codecov.io/
NUGET_PACKAGES: $(Agent.TempDirectory)/.nuget/packages
jobs:
- template: azure-pipelines/build.yml
parameters:
SignType:
ShouldSkipOptimize:

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

@ -1,6 +1,6 @@
<#
.SYNOPSIS
Builds all projects in this repo.
Converts between Windows PDB and Portable PDB formats.
.PARAMETER DllPath
The path to the DLL whose PDB is to be converted.
.PARAMETER PdbPath

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

@ -0,0 +1 @@
<RunSettings />

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

@ -0,0 +1,14 @@
<#
.SYNOPSIS
Downloads 32-bit and 64-bit procdump executables and returns the path to where they were installed.
#>
$version = '0.0.1'
$baseDir = "$PSScriptRoot\..\obj\tools"
$procDumpToolPath = "$baseDir\procdump.$version\bin"
if (-not (Test-Path $procDumpToolPath)) {
if (-not (Test-Path $baseDir)) { New-Item -Type Directory -Path $baseDir | Out-Null }
$baseDir = (Resolve-Path $baseDir).Path # Normalize it
& (& $PSScriptRoot\Get-NuGetTool.ps1) install procdump -version $version -PackageSaveMode nuspec -OutputDirectory $baseDir -Source https://api.nuget.org/v3/index.json | Out-Null
}
(Resolve-Path $procDumpToolPath).Path

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

@ -1,7 +1,7 @@
if ($env:AGENT_TOOLSDIRECTORY) {
$path = "$env:AGENT_TOOLSDIRECTORY\vs-platform\tools"
if ($env:AGENT_TEMPDIRECTORY) {
$path = "$env:AGENT_TEMPDIRECTORY\$env:BUILD_BUILDID"
} elseif ($env:localappdata) {
$path = "$env:localappdata\vs-platform\tools"
$path = "$env:localappdata\gitrepos\tools"
} else {
$path = "$PSScriptRoot\..\obj\tools"
}

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

@ -10,11 +10,7 @@ if ($existingTool) {
return $existingTool.Path
}
if ($env:AGENT_TEMPDIRECTORY) {
$toolInstallDir = "$env:AGENT_TEMPDIRECTORY/$env:BUILD_BUILDID"
} else {
$toolInstallDir = "$PSScriptRoot/../obj/tools"
}
$toolInstallDir = & "$PSScriptRoot/Get-TempToolsPath.ps1"
$toolPath = "$toolInstallDir/nbgv"
if (!(Test-Path $toolInstallDir)) { New-Item -Path $toolInstallDir -ItemType Directory | Out-Null }

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

@ -0,0 +1 @@
<RunSettings />

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

@ -1,20 +1,21 @@
trigger: none
pr: none
schedules:
- cron: "0 3 * * Tue" # Mon @ 8 or 9 PM Mountain Time (depending on DST)
displayName: Weekly OptProf run
branches:
include:
- 'v16.*'
- master
always: true # we must keep data fresh since optimizationdata drops are purged after 30 days
- cron: "0 3 * * Tue" # Mon @ 8 or 9 PM Mountain Time (depending on DST)
displayName: Weekly OptProf run
branches:
include:
- 'v1*'
- master
always: true # we must keep data fresh since optimizationdata drops are purged after 30 days
resources:
repositories:
- repository: scripts
type: git
name: DeploymentScripts
ref: refs/heads/test
# Avoid errant CI builds: https://developercommunity.visualstudio.com/content/problem/1154409/azure-pipeline-is-triggering-due-to-events-that-ne.html
#resources:
# repositories:
# - repository: scripts
# type: git
# name: DeploymentScripts
# ref: refs/heads/test
variables:
- name: TreatWarningsAsErrors
@ -31,6 +32,8 @@ variables:
value: true
- name: PublicRelease
value: false # avoid using nice version since we're building a preliminary/unoptimized package
- name: SignType
value: real
- group: Library.Template
stages:
@ -41,12 +44,11 @@ stages:
- template: build.yml
parameters:
windowsPool: VSEng-MicroBuildVS2019
SignType: real
ShouldSkipOptimize: true
- stage: QueueVSBuild
jobs:
- job: QueueOptProf
pool: VSEng-RMPool
pool: VSEng-ReleasePool
variables:
InsertPayloadName: vs-threading
InsertTopicBranch: team/VS-IDE/vs-threading-OptProf-run-$(Build.BuildId)

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

@ -1,39 +0,0 @@
<#
.SYNOPSIS
Set environment variables in the environment.
Azure Pipeline and CMD environments are considered.
.PARAMETER Variables
A hashtable of variables to be set.
.OUTPUTS
A boolean indicating whether the environment variables can be expected to propagate to the caller's environment.
#>
[CmdletBinding(SupportsShouldProcess=$true)]
Param(
[Parameter(Mandatory=$true, Position=1)]
$Variables
)
if ($Variables.Count -eq 0) {
return $true
}
$cmdInstructions = !$env:TF_BUILD -and $env:PS1UnderCmd -eq '1'
if ($cmdInstructions) {
Write-Warning "Environment variables have been set that will be lost because you're running under cmd.exe"
Write-Host "Environment variables that must be set manually:" -ForegroundColor Blue
}
$Variables.GetEnumerator() |% {
Set-Item -Path env:$($_.Key) -Value $_.Value
# If we're running in Azure Pipelines, set these environment variables
if ($env:TF_BUILD) {
Write-Host "##vso[task.setvariable variable=$($_.Key);]$($_.Value)"
}
if ($cmdInstructions) {
Write-Host "SET $($_.Key)=$($_.Value)"
}
}
return !$cmdInstructions

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

@ -0,0 +1,14 @@
<RunSettings>
<DataCollectionRunSettings>
<DataCollectors>
<DataCollector friendlyName="blame" enabled="True">
<Configuration>
<CollectDump DumpType="full" />
<CollectDumpOnTestSessionHang TestTimeout="30000" DumpType="full" />
<!-- This ResultsDirectory must exist, but it is totally ignored. -->
<ResultsDirectory>%BUILD_ARTIFACTSTAGINGDIRECTORY%</ResultsDirectory>
</Configuration>
</DataCollector>
</DataCollectors>
</DataCollectionRunSettings>
</RunSettings>

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

@ -2,18 +2,21 @@
# It "snaps" the values of these variables where we can compute them during the build,
# and otherwise captures the scripts to run later during an Azure Pipelines environment release.
$RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..")
$ArtifactBasePath = "$RepoRoot\obj\_artifacts"
$VariablesArtifactPath = "$ArtifactBasePath\variables"
$RepoRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot (Join-Path .. ..)))
$ArtifactBasePath = Join-Path $RepoRoot (Join-Path obj _artifacts)
$VariablesArtifactPath = Join-Path $ArtifactBasePath variables
if (-not (Test-Path $VariablesArtifactPath)) { New-Item -ItemType Directory -Path $VariablesArtifactPath | Out-Null }
# Copy variables, either by value if the value is calculable now, or by script
Get-ChildItem -Path "$PSScriptRoot\..\variables" |% {
Get-ChildItem -Path (Join-Path $PSScriptRoot (Join-Path .. variables)) |% {
$value = $null
if (-not $_.BaseName.StartsWith('_')) { # Skip trying to interpret special scripts
# First check the environment variables in case the variable was set in a queued build
if (Test-Path env:$($_.BaseName)) {
$value = Get-Content "env:$($_.BaseName)"
# Always use all caps for env var access because Azure Pipelines converts variables to upper-case for env vars,
# and on non-Windows env vars are case sensitive.
$envVarName = $_.BaseName.ToUpper()
if (Test-Path env:$envVarName) {
$value = Get-Content "env:$envVarName"
}
# If that didn't give us anything, try executing the script right now from its original position
@ -32,7 +35,7 @@ Get-ChildItem -Path "$PSScriptRoot\..\variables" |% {
$value = Get-Content -Path $_.FullName
}
Set-Content -Path "$VariablesArtifactPath\$($_.Name)" -Value $value
Set-Content -Path (Join-Path $VariablesArtifactPath $_.Name) -Value $value
}
@{

5
azure-pipelines/artifacts/_all.ps1 Normal file → Executable file
Просмотреть файл

@ -1,3 +1,5 @@
#!/usr/bin/env pwsh
# This script returns all the artifacts that should be collected after a build.
#
# Each powershell artifact is expressed as an object with these properties:
@ -22,14 +24,13 @@ Function EnsureTrailingSlash($path) {
Get-ChildItem "$PSScriptRoot\*.ps1" -Exclude "_*" -Recurse |% {
$ArtifactName = $_.BaseName
Write-Host "Collecting files for the $ArtifactName artifact"
$fileGroups = & $_
if (!$fileGroups -or $fileGroups.Count -eq 0) {
Write-Warning "No files found for the `"$ArtifactName`" artifact."
} else {
$fileGroups.GetEnumerator() | % {
$BaseDirectory = New-Object Uri ((EnsureTrailingSlash $_.Key), [UriKind]::Absolute)
$BaseDirectory = New-Object Uri ((EnsureTrailingSlash $_.Key.ToString()), [UriKind]::Absolute)
$_.Value | % {
if ($_.GetType() -eq [IO.FileInfo] -or $_.GetType() -eq [IO.DirectoryInfo]) {
$_ = $_.FullName

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

@ -5,51 +5,6 @@ param (
[string]$ArtifactNameSuffix
)
$RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..")
if ($env:BUILD_ARTIFACTSTAGINGDIRECTORY) {
$ArtifactStagingFolder = $env:BUILD_ARTIFACTSTAGINGDIRECTORY
} else {
$ArtifactStagingFolder = "$RepoRoot\obj\_artifacts"
if (Test-Path $ArtifactStagingFolder) {
Remove-Item $ArtifactStagingFolder -Recurse -Force
}
}
function Create-SymbolicLink {
param (
$Link,
$Target
)
if ($Link -eq $Target) {
return
}
if (Test-Path $Link) { Remove-Item $Link }
$LinkContainer = Split-Path $Link -Parent
if (!(Test-Path $LinkContainer)) { mkdir $LinkContainer }
Write-Verbose "Linking $Link to $Target"
if ($IsMacOS -or $IsLinux) {
ln $Target $Link | Out-Null
} else {
cmd /c "mklink `"$Link`" `"$Target`"" | Out-Null
}
}
# Stage all artifacts
$Artifacts = & "$PSScriptRoot\_all.ps1"
$Artifacts |% {
$DestinationFolder = (Join-Path (Join-Path $ArtifactStagingFolder "$($_.ArtifactName)$ArtifactNameSuffix") $_.ContainerFolder).TrimEnd('\')
$Name = "$(Split-Path $_.Source -Leaf)"
#Write-Host "$($_.Source) -> $($_.ArtifactName)\$($_.ContainerFolder)" -ForegroundColor Yellow
if (-not (Test-Path $DestinationFolder)) { New-Item -ItemType Directory -Path $DestinationFolder | Out-Null }
if (Test-Path -PathType Leaf $_.Source) { # skip folders
Create-SymbolicLink -Link "$DestinationFolder\$Name" -Target $_.Source
}
}
$Artifacts |% { $_.ArtifactName } | Get-Unique |% {
Write-Host "##vso[artifact.upload containerfolder=$_$ArtifactNameSuffix;artifactname=$_$ArtifactNameSuffix;]$ArtifactStagingFolder/$_$ArtifactNameSuffix"
& "$PSScriptRoot/_stage_all.ps1" -ArtifactNameSuffix $ArtifactNameSuffix |% {
Write-Host "##vso[artifact.upload containerfolder=$($_.Name);artifactname=$($_.Name);]$($_.Path)"
}

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

@ -0,0 +1,59 @@
# This script links all the artifacts described by _all.ps1
# into a staging directory, reading for uploading to a cloud build artifact store.
# It returns a sequence of objects with Name and Path properties.
param (
[string]$ArtifactNameSuffix
)
$RepoRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot (Join-Path .. ..)))
if ($env:BUILD_ARTIFACTSTAGINGDIRECTORY) {
$ArtifactStagingFolder = $env:BUILD_ARTIFACTSTAGINGDIRECTORY
} else {
$ArtifactStagingFolder = Join-Path $RepoRoot (Join-Path obj _artifacts)
if (Test-Path $ArtifactStagingFolder) {
Remove-Item $ArtifactStagingFolder -Recurse -Force
}
}
function Create-SymbolicLink {
param (
$Link,
$Target
)
if ($Link -eq $Target) {
return
}
if (Test-Path $Link) { Remove-Item $Link }
$LinkContainer = Split-Path $Link -Parent
if (!(Test-Path $LinkContainer)) { mkdir $LinkContainer }
Write-Verbose "Linking $Link to $Target"
if ($IsMacOS -or $IsLinux) {
ln $Target $Link | Out-Null
} else {
cmd /c "mklink `"$Link`" `"$Target`"" | Out-Null
}
}
# Stage all artifacts
$Artifacts = & "$PSScriptRoot\_all.ps1"
$Artifacts |% {
$DestinationFolder = (Join-Path (Join-Path $ArtifactStagingFolder "$($_.ArtifactName)$ArtifactNameSuffix") $_.ContainerFolder).TrimEnd('\')
$Name = "$(Split-Path $_.Source -Leaf)"
#Write-Host "$($_.Source) -> $($_.ArtifactName)\$($_.ContainerFolder)" -ForegroundColor Yellow
if (-not (Test-Path $DestinationFolder)) { New-Item -ItemType Directory -Path $DestinationFolder | Out-Null }
if (Test-Path -PathType Leaf $_.Source) { # skip folders
Create-SymbolicLink -Link (Join-Path $DestinationFolder $Name) -Target $_.Source
}
}
$Artifacts |% { "$($_.ArtifactName)$ArtifactNameSuffix" } | Get-Unique |% {
$artifact = New-Object -TypeName PSObject
Add-Member -InputObject $artifact -MemberType NoteProperty -Name Name -Value $_
Add-Member -InputObject $artifact -MemberType NoteProperty -Name Path -Value (Join-Path $ArtifactStagingFolder $_)
Write-Output $artifact
}

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

@ -0,0 +1,12 @@
if ($env:AGENT_TEMPDIRECTORY) {
# The DotNetCoreCLI uses an alternate location to publish these files
$guidRegex = '^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$'
@{
$env:AGENT_TEMPDIRECTORY = (Get-ChildItem $env:AGENT_TEMPDIRECTORY -Directory |? { $_.Name -match $guidRegex } |% { Get-ChildItem "$($_.FullName)\testhost*.dmp","$($_.FullName)\Sequence_*.xml" -Recurse });
}
} else {
$testRoot = Resolve-Path "$PSScriptRoot\..\..\test"
@{
$testRoot = (Get-ChildItem "$testRoot\TestResults" -Recurse -Directory | Get-ChildItem -Recurse -File);
}
}

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

@ -2,7 +2,6 @@ parameters:
- name: windowsPool
type: string
default: Hosted Windows 2019 with VS2019
- name: SignType
- name: ShouldSkipOptimize
jobs:
@ -13,13 +12,12 @@ jobs:
clean: true
- template: install-dependencies.yml
- powershell: '& (./azure-pipelines/Get-nbgv.ps1) cloud -p src'
- powershell: '& (./azure-pipelines/Get-nbgv.ps1) cloud'
displayName: Set build number
- ${{ if eq(variables['system.collectionId'], '011b8bdf-6d56-4f87-be0d-0092136884d9') }}:
- template: microbuild.before.yml
parameters:
SignType: ${{ parameters.SignType }}
ShouldSkipOptimize: ${{ parameters.ShouldSkipOptimize }}
- template: dotnet.yml
@ -30,7 +28,7 @@ jobs:
- job: Linux
condition: ne(variables['OptProf'], 'true')
pool:
vmImage: Ubuntu 16.04
vmImage: Ubuntu 18.04
steps:
- checkout: self
clean: true
@ -53,8 +51,8 @@ jobs:
- Linux
- macOS
pool:
vmImage: Ubuntu 16.04
condition: ne(variables['OptProf'], 'true')
vmImage: Ubuntu 18.04
condition: succeededOrFailed()
steps:
- checkout: self
clean: true

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

@ -1,44 +1,41 @@
steps:
# We use VSBuild instead of "dotnet build" on Windows where MicroBuild tasks have to run (since they don't support MSBuild Core yet).
- task: VSBuild@1
displayName: Build Visual Studio solution
inputs:
msbuildArgs: /t:build,pack /m /bl:"$(Build.ArtifactStagingDirectory)/build_logs/msbuild.binlog"
platform: Any CPU
configuration: $(BuildConfiguration)
displayName: Build Visual Studio solution
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
- script: dotnet build /t:build,pack --no-restore -c $(BuildConfiguration) /p:Platform=NonWindows /v:m /bl:"$(Build.ArtifactStagingDirectory)/build_logs/build.binlog"
displayName: dotnet build and pack
workingDirectory: src
displayName: dotnet build & pack
condition: and(succeeded(), ne(variables['Agent.OS'], 'Windows_NT'))
- task: DotNetCoreCLI@2
displayName: dotnet test -f net472
inputs:
command: test
arguments: --no-build -c $(BuildConfiguration) -f net472 --filter "TestCategory!=FailsInCloudTest" -v n /p:CollectCoverage=true
arguments: --no-build -c $(BuildConfiguration) -f net472 --filter "TestCategory!=FailsInCloudTest" -v n /p:CollectCoverage=true --settings "$(Build.Repository.LocalPath)/azure-pipelines/$(Agent.OS).runsettings"
testRunTitle: net472-$(Agent.JobName)
workingDirectory: src
condition: and(ne(variables['OptProf'], 'true'), eq(variables['Agent.OS'], 'Windows_NT'))
- task: DotNetCoreCLI@2
displayName: dotnet test -f netcoreapp2.1
inputs:
command: test
arguments: --no-build -c $(BuildConfiguration) -f netcoreapp2.1 --filter "TestCategory!=FailsInCloudTest" -v n /p:CollectCoverage=true
arguments: --no-build -c $(BuildConfiguration) -f netcoreapp2.1 --filter "TestCategory!=FailsInCloudTest" -v n /p:CollectCoverage=true --settings "$(Build.Repository.LocalPath)/azure-pipelines/$(Agent.OS).runsettings"
testRunTitle: netcoreapp2.1-$(Agent.JobName)
workingDirectory: src/Microsoft.VisualStudio.Threading.Tests
workingDirectory: test/Microsoft.VisualStudio.Threading.Tests
condition: ne(variables['OptProf'], 'true')
- task: DotNetCoreCLI@2
displayName: dotnet test -f netcoreapp3.1
inputs:
command: test
arguments: --no-build -c $(BuildConfiguration) -f netcoreapp3.1 --filter "TestCategory!=FailsInCloudTest" -v n /p:CollectCoverage=true
arguments: --no-build -c $(BuildConfiguration) -f netcoreapp3.1 --filter "TestCategory!=FailsInCloudTest" -v n /p:CollectCoverage=true --settings "$(Build.Repository.LocalPath)/azure-pipelines/$(Agent.OS).runsettings"
testRunTitle: netcoreapp3.1-$(Agent.JobName)
workingDirectory: src/Microsoft.VisualStudio.Threading.Tests
workingDirectory: test/Microsoft.VisualStudio.Threading.Tests
condition: ne(variables['OptProf'], 'true')
# We have to artifically run this script so that the extra .nupkg is produced for variables/InsertConfigValues.ps1 to notice.
@ -46,23 +43,19 @@ steps:
displayName: Prepare VSInsertion artifact
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
- task: PowerShell@2
inputs:
filePath: azure-pipelines/variables/_pipelines.ps1
failOnStderr: true
- powershell: azure-pipelines/variables/_pipelines.ps1
failOnStderr: true
displayName: Update pipeline variables based on build outputs
condition: succeededOrFailed()
- task: PowerShell@2
inputs:
filePath: azure-pipelines/artifacts/_pipelines.ps1
arguments: -ArtifactNameSuffix "-$(Agent.JobName)"
- powershell: azure-pipelines/artifacts/_pipelines.ps1 -ArtifactNameSuffix "-$(Agent.JobName)"
failOnStderr: true
displayName: Publish artifacts
condition: succeededOrFailed()
- task: PublishSymbols@2
inputs:
SymbolsFolder: $(Build.ArtifactStagingDirectory)/symbols-Windows
SymbolsFolder: $(Build.ArtifactStagingDirectory)/symbols-$(Agent.JobName)
SearchPattern: '**/*.pdb'
IndexSources: false
SymbolServerType: TeamServices

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

@ -4,7 +4,8 @@ parameters:
steps:
- powershell: |
.\init.ps1 -AccessToken '$(System.AccessToken)' ${{ parameters['initArgs'] }}
$AccessToken = '$(System.AccessToken)' # Avoid specifying the access token directly on the init.ps1 command line to avoid it showing up in errors
.\init.ps1 -AccessToken $AccessToken ${{ parameters['initArgs'] }} -UpgradePrerequisites
dotnet --info
displayName: Install prerequisites
@ -14,8 +15,7 @@ steps:
inputs:
versionSpec: 5.1.x
- task: PowerShell@2
inputs:
filePath: azure-pipelines/variables/_pipelines.ps1
failOnStderr: true
- powershell: azure-pipelines/variables/_pipelines.ps1
failOnStderr: true
displayName: Set pipeline variables based on source
name: SetPipelineVariables

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

@ -1,4 +1,10 @@
steps:
- task: ms-vseng.MicroBuildShipTasks.7c429315-71ba-4cb3-94bb-f829c95f7915.MicroBuildCodesignVerify@2
displayName: Verify Signed Files
inputs:
TargetFolders: |
$(Build.SourcesDirectory)/bin/Packages/$(BuildConfiguration)/NuGet
- task: MicroBuildCleanup@1
condition: succeededOrFailed()
displayName: MicroBuild Cleanup
@ -36,5 +42,5 @@ steps:
/repoName:$(Build.Repository.Name)
/additionalCodexArguments:-bld
/additionalCodexArguments:$(Build.ArtifactStagingDirectory)/build_logs
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'), ne(variables['Build.Reason'], 'PullRequest'))
continueOnError: true

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

@ -1,5 +1,4 @@
parameters:
- name: SignType
- name: ShouldSkipOptimize
steps:
@ -13,6 +12,6 @@ steps:
- task: MicroBuildSigningPlugin@2
inputs:
signType: ${{ parameters.SignType }}
signType: $(SignType)
zipSources: false
displayName: Install MicroBuild Signing Plugin

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

@ -5,10 +5,21 @@ trigger:
- 'v16.*'
- 'validate/*'
paths:
exclude: [".github", "doc", "*.md", ".appveyor.yml", ".travis.yml"]
exclude:
- .github/
- doc/
- '*.md'
- .vscode/
schedules:
- cron: "0 3 * * *" # Daily @ 8 PM PST
displayName: Daily vs-insertion
branches:
include:
- master
- 'v16.*'
parameters:
- name: SignType
- name: SignTypeSelection
displayName: Sign type
type: string
default: Test
@ -18,18 +29,75 @@ parameters:
type: boolean
default: false
variables:
TreatWarningsAsErrors: true
UpdateXlfOnBuild: false # force build breaks if xlf files aren't updated on dev box with resx changes
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
BuildConfiguration: Release
BuildPlatform: Any CPU
NUGET_PACKAGES: $(Agent.TempDirectory)/.nuget/packages
SignType: ${{ parameters.SignType }}
stages:
jobs:
- template: build.yml
parameters:
windowsPool: VSEng-MicroBuildVS2019
SignType: ${{ parameters.SignType }}
ShouldSkipOptimize: ${{ parameters.ShouldSkipOptimize }}
- stage: Build
variables:
TreatWarningsAsErrors: true
UpdateXlfOnBuild: false # force build breaks if xlf files aren't updated on dev box with resx changes
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
BuildConfiguration: Release
BuildPlatform: Any CPU
NUGET_PACKAGES: $(Agent.TempDirectory)/.nuget/packages
SignTypeSelection: ${{ parameters.SignTypeSelection }}
jobs:
- template: build.yml
parameters:
windowsPool: VSEng-MicroBuildVS2019
ShouldSkipOptimize: ${{ parameters.ShouldSkipOptimize }}
- stage: symbol_archive
displayName: Symbol archival
condition: and(succeeded(), eq(dependencies.Build.outputs['Windows.SetPipelineVariables.SignType'], 'Real'))
jobs:
- job: archive
pool: VSEng-ReleasePool
steps:
- download: current
artifact: Variables-Windows
displayName: Download Variables-Windows artifact
- task: PowerShell@2
displayName: Set VSTS variables based on artifacts
inputs:
targetType: filePath
filePath: $(Pipeline.Workspace)/Variables-Windows/_pipelines.ps1
- download: current
artifact: symbols-Windows
displayName: Download symbols-Windows artifact
- task: MicroBuildArchiveSymbols@1
displayName: Archive $(SymbolsFeatureName) on Symweb
inputs:
SymbolsFeatureName: $(SymbolsFeatureName)
SymbolsSymwebProject: VS
SymbolsUncPath: \\cpvsbuild\drops\$(TeamName)\$(Build.DefinitionName)\$(Build.SourceBranchName)\$(Build.BuildNumber)\Symbols.Archival
SymbolsEmailContacts: vsidemicrobuild
SymbolsAgentPath: $(Pipeline.Workspace)/symbols-Windows
- task: MicroBuildCleanup@1
displayName: Send Telemetry
- stage: azure_public_vssdk_feed
displayName: azure-public/vssdk feed
condition: and(succeeded(), eq(dependencies.Build.outputs['Windows.SetPipelineVariables.SignType'], 'Real'))
jobs:
- deployment: push
pool:
vmImage: ubuntu-latest
environment: No-Approval
strategy:
runOnce:
deploy:
steps:
- download: current
artifact: deployables-Windows
displayName: Download deployables-Windows artifact
- task: NuGetToolInstaller@1
displayName: Use NuGet 5.x
inputs:
versionSpec: 5.x
- task: NuGetCommand@2
displayName: NuGet push
inputs:
command: push
packagesToPush: $(Pipeline.Workspace)/deployables-Windows/NuGet/*.nupkg
nuGetFeedType: external
publishFeedCredentials: azure-public/vssdk

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

@ -3,6 +3,11 @@ steps:
displayName: Download deployables
artifact: deployables-Windows
- task: NuGetToolInstaller@1
displayName: Use NuGet 5.x
inputs:
versionSpec: 5.x
- task: NuGetCommand@2
displayName: Push packages to CI feed
inputs:

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

@ -0,0 +1,9 @@
steps:
- download: CI
artifact: Variables-Windows
displayName: Download Variables-Windows artifact
- task: PowerShell@2
displayName: Set VSTS variables based on artifacts
inputs:
targetType: filePath
filePath: $(Pipeline.Workspace)/CI/Variables-Windows/_pipelines.ps1

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

@ -0,0 +1,72 @@
trigger: none # We only want to trigger manually or based on resources
pr: none
resources:
pipelines:
- pipeline: CI
source: Library # TODO: This should match the name of your CI pipeline
trigger:
tags:
- auto-release
stages:
- stage: GitHubRelease
displayName: GitHub Release
jobs:
- deployment: create
pool:
vmImage: ubuntu-latest
environment: No-Approval
strategy:
runOnce:
deploy:
steps:
- download: none
- powershell: |
Write-Host "##vso[build.updatebuildnumber]$(resources.pipeline.CI.runName)"
displayName: Set pipeline name
- task: GitHubRelease@1
displayName: GitHub release (create)
inputs:
gitHubConnection: AArnott
repositoryName: $(Build.Repository.Name)
target: $(resources.pipeline.CI.sourceCommit)
tagSource: userSpecifiedTag
tag: v$(resources.pipeline.CI.runName)
title: v$(resources.pipeline.CI.runName)
isDraft: true # After running this step, visit the new draft release, edit, and publish.
changeLogCompareToRelease: lastNonDraftRelease
changeLogType: issueBased
changeLogLabels: |
[
{ "label" : "bug", "displayName" : "Fixes", "state" : "closed" },
{ "label" : "enhancement", "displayName": "Enhancements", "state" : "closed" }
]
- stage: nuget_org
displayName: nuget.org
dependsOn: GitHubRelease
jobs:
- deployment: push
pool:
vmImage: ubuntu-latest
environment: No-Approval
strategy:
runOnce:
deploy:
steps:
- download: CI
artifact: deployables-Windows
displayName: Download deployables-Windows artifact
patterns: 'deployables-Windows/*'
- task: NuGetToolInstaller@1
displayName: Use NuGet 5.x
inputs:
versionSpec: 5.x
- task: NuGetCommand@2
displayName: NuGet push
inputs:
command: push
packagesToPush: $(Pipeline.Workspace)/CI/deployables-Windows/NuGet/*.nupkg
nuGetFeedType: external
publishFeedCredentials: VisualStudioExtensibility (nuget.org)

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

@ -0,0 +1,3 @@
steps:
- powershell: echo "##vso[build.addbuildtag]auto-insertion"
displayName: Tag for auto-insertion

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

@ -1,4 +1,4 @@
$BinPath = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..\bin\Packages\$env:BuildConfiguration")
$BinPath = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..\bin\Packages\$env:BUILDCONFIGURATION")
$dirsToSearch = "$BinPath\NuGet\*.nupkg","$BinPath\CoreXT\*.nupkg" |? { Test-Path $_ }
$icv=@()

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

@ -0,0 +1 @@
'Andrew Arnott'

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

@ -0,0 +1 @@
'false'

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

@ -0,0 +1,12 @@
if ($env:SYSTEM_COLLECTIONID -eq '011b8bdf-6d56-4f87-be0d-0092136884d9') {
if ($env:BUILD_REASON -eq 'Schedule') {
'real'
} else {
if ($env:SignTypeSelection) {
$env:SignTypeSelection
} else {
'test'
}
}
}

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

@ -0,0 +1 @@
'vsidemicrobuild@microsoft.com'

2
azure-pipelines/variables/_all.ps1 Normal file → Executable file
Просмотреть файл

@ -1,3 +1,5 @@
#!/usr/bin/env pwsh
# This script returns a hashtable of build variables that should be set
# at the start of a build or release definition's execution.

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

@ -5,11 +5,20 @@
# what the build would do. So only set them if they have not already been set.
(& "$PSScriptRoot\_all.ps1").GetEnumerator() |% {
if (Test-Path -Path "env:$($_.Key.ToUpper())") {
Write-Host "Skipping setting $($_.Key) because variable is already set." -ForegroundColor Cyan
# Always use ALL CAPS for env var names since Azure Pipelines converts variable names to all caps and on non-Windows OS, env vars are case sensitive.
$keyCaps = $_.Key.ToUpper()
if (Test-Path -Path "env:$keyCaps") {
Write-Host "Skipping setting $keyCaps because variable is already set to '$(Get-Content env:$keyCaps)'." -ForegroundColor Cyan
} else {
Write-Host "$($_.Key)=$($_.Value)" -ForegroundColor Yellow
Write-Host "##vso[task.setvariable variable=$($_.Key);]$($_.Value)"
Set-Item -Path "env:$($_.Key)" -Value $_.Value
Write-Host "$keyCaps=$($_.Value)" -ForegroundColor Yellow
if ($env:TF_BUILD) {
# Create two variables: the first that can be used by its simple name and accessible only within this job.
Write-Host "##vso[task.setvariable variable=$keyCaps]$($_.Value)"
# and the second that works across jobs and stages but must be fully qualified when referenced.
Write-Host "##vso[task.setvariable variable=$keyCaps;isOutput=true]$($_.Value)"
} elseif ($env:GITHUB_ACTIONS) {
Write-Host "::set-env name=$keyCaps::$($_.Value)"
}
Set-Item -Path "env:$keyCaps" -Value $_.Value
}
}

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

@ -0,0 +1,50 @@
trigger: none # We only want to trigger manually or based on resources
pr: none
resources:
pipelines:
- pipeline: CI
source: vs-threading
tags:
- Real signed
trigger:
tags:
- Real signed
- auto-insertion
stages:
- stage: VS
displayName: VS insertion
jobs:
- deployment: insertion
pool: VSEng-ReleasePool
environment: No-Approval
strategy:
runOnce:
deploy:
steps:
- powershell: |
Write-Host "##vso[build.updatebuildnumber]$(resources.pipeline.CI.runName)"
displayName: Set pipeline name
- template: release-deployment-prep.yml
- download: CI
artifact: VSInsertion-Windows
displayName: Download VSInsertion-Windows artifact
- task: NuGetCommand@2
displayName: Push CoreXT packages to VS feed
inputs:
command: push
packagesToPush: $(Pipeline.Workspace)/CI/VSInsertion-windows/*.nupkg
publishVstsFeed: 97a41293-2972-4f48-8c0e-05493ae82010
allowPackageConflicts: true
- task: MicroBuildInsertVsPayload@3
displayName: Insert VS Payload
inputs:
TeamName: $(TeamName)
TeamEmail: $(TeamEmail)
InsertionPayloadName: $(Build.Repository.Name) $(Build.BuildNumber)
InsertionBuildPolicy: Request Perf DDRITs
AutoCompletePR: true
AutoCompleteMergeStrategy: Squash
- task: MicroBuildCleanup@1
displayName: Send Telemetry

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

@ -95,7 +95,7 @@ public static class LibrarySettings
{
get
{
if (joinableTaskContext == null)
if (joinableTaskContext is null)
{
// This self-initializer is for when an app does not have a `JoinableTaskContext` to pass to the library.
// Our private instance will only work if this property getter first runs on the main thread of the application
@ -108,7 +108,7 @@ public static class LibrarySettings
set
{
Assumes.True(joinableTaskContext == null || joinableTaskContext == value, "This property has already been set to another value or is set after its value has been retrieved with a self-created value. Set this property once, before it is used elsewhere.");
Assumes.True(joinableTaskContext is null || joinableTaskContext == value, "This property has already been set to another value or is set after its value has been retrieved with a self-created value. Set this property once, before it is used elsewhere.");
joinableTaskContext = value;
}
}
@ -117,7 +117,7 @@ public static class LibrarySettings
This pattern and self-initializer allows all the rest of your library code to assume JTF is always present (so you can use JTF.Run and JTF.RunAsync everywhere w/o feature that JTF will be null), and it mitigates all the deadlocks possible given the host constraints.
Note that when you create your own default instance of JoinableTaskContext (i.e. when the host doesn't), it will consider the thread you're on to be the main thread. If SynchronizationContext.Current != null it will capture it and use it to switch to the main thread when you ask it to (very similar to how VS works today), otherwise any request to SwitchToMainThreadAsync will never switch the thread (since no `SynchronizationContext` was supplied to do so) but otherwise JTF continues to work.
Note that when you create your own default instance of JoinableTaskContext (i.e. when the host doesn't), it will consider the thread you're on to be the main thread. If SynchronizationContext.Current is object it will capture it and use it to switch to the main thread when you ask it to (very similar to how VS works today), otherwise any request to SwitchToMainThreadAsync will never switch the thread (since no `SynchronizationContext` was supplied to do so) but otherwise JTF continues to work.
## <a name="ctor"></a>Accept `JoinableTaskContext` as a constructor argument

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

@ -1,7 +1,7 @@
{
"sdk": {
"version": "3.1.100",
"rollForward": "latestPatch",
"version": "3.1.302",
"rollForward": "patch",
"allowPrerelease": false
},
"msbuild-sdks": {

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

@ -1,3 +1,20 @@
@set PS1UnderCmd=1
powershell.exe -ExecutionPolicy bypass -Command "& '%~dpn0.ps1'" %*
@set PS1UnderCmd=
@echo off
SETLOCAL
set PS1UnderCmd=1
:: Get the datetime in a format that can go in a filename.
set _my_datetime=%date%_%time%
set _my_datetime=%_my_datetime: =_%
set _my_datetime=%_my_datetime::=%
set _my_datetime=%_my_datetime:/=_%
set _my_datetime=%_my_datetime:.=_%
set CmdEnvScriptPath=%temp%\envvarscript_%_my_datetime%.cmd
powershell.exe -NoProfile -NoLogo -ExecutionPolicy bypass -Command "try { & '%~dpn0.ps1' %*; exit $LASTEXITCODE } catch { write-host $_; exit 1 }"
:: Set environment variables in the parent cmd.exe process.
IF EXIST "%CmdEnvScriptPath%" (
ENDLOCAL
CALL "%CmdEnvScriptPath%"
DEL "%CmdEnvScriptPath%"
)

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

@ -1,27 +1,36 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Installs dependencies required to build and test the projects in this repository.
Installs dependencies required to build and test the projects in this repository.
.DESCRIPTION
This MAY not require elevation, as the SDK and runtimes are installed locally to this repo location,
unless `-InstallLocality machine` is specified.
This MAY not require elevation, as the SDK and runtimes are installed to a per-user location,
unless the `-InstallLocality` switch is specified directing to a per-repo or per-machine location.
See detailed help on that switch for more information.
The CmdEnvScriptPath environment variable may be optionally set to a path to a cmd shell script to be created (or appended to if it already exists) that will set the environment variables in cmd.exe that are set within the PowerShell environment.
This is used by init.cmd in order to reapply any new environment variables to the parent cmd.exe process that were set in the powershell child process.
.PARAMETER InstallLocality
A value indicating whether dependencies should be installed locally to the repo or at a per-user location.
Per-user allows sharing the installed dependencies across repositories and allows use of a shared expanded package cache.
Visual Studio will only notice and use these SDKs/runtimes if VS is launched from the environment that runs this script.
Per-repo allows for high isolation, allowing for a more precise recreation of the environment within an Azure Pipelines build.
When using 'repo', environment variables are set to cause the locally installed dotnet SDK to be used.
Per-repo can lead to file locking issues when dotnet.exe is left running as a build server and can be mitigated by running `dotnet build-server shutdown`.
Per-machine requires elevation and will download and install all SDKs and runtimes to machine-wide locations so all applications can find it.
A value indicating whether dependencies should be installed locally to the repo or at a per-user location.
Per-user allows sharing the installed dependencies across repositories and allows use of a shared expanded package cache.
Visual Studio will only notice and use these SDKs/runtimes if VS is launched from the environment that runs this script.
Per-repo allows for high isolation, allowing for a more precise recreation of the environment within an Azure Pipelines build.
When using 'repo', environment variables are set to cause the locally installed dotnet SDK to be used.
Per-repo can lead to file locking issues when dotnet.exe is left running as a build server and can be mitigated by running `dotnet build-server shutdown`.
Per-machine requires elevation and will download and install all SDKs and runtimes to machine-wide locations so all applications can find it.
.PARAMETER NoPrerequisites
Skips the installation of prerequisite software (e.g. SDKs, tools).
Skips the installation of prerequisite software (e.g. SDKs, tools).
.PARAMETER UpgradePrerequisites
Takes time to install prerequisites even if they are already present in case they need to be upgraded.
No effect if -NoPrerequisites is specified.
.PARAMETER NoRestore
Skips the package restore step.
Skips the package restore step.
.PARAMETER Signing
Install the MicroBuild signing plugin for building test-signed builds on desktop machines.
Install the MicroBuild signing plugin for building test-signed builds on desktop machines.
.PARAMETER OptProf
Install the MicroBuild OptProf plugin for building optimized assemblies on desktop machines.
Install the MicroBuild OptProf plugin for building optimized assemblies on desktop machines.
.PARAMETER AccessToken
An optional access token for authenticating to Azure Artifacts authenticated feeds.
An optional access token for authenticating to Azure Artifacts authenticated feeds.
#>
[CmdletBinding(SupportsShouldProcess=$true)]
Param (
@ -30,6 +39,8 @@ Param (
[Parameter()]
[switch]$NoPrerequisites,
[Parameter()]
[switch]$UpgradePrerequisites,
[Parameter()]
[switch]$NoRestore,
[Parameter()]
[switch]$Signing,
@ -39,9 +50,20 @@ Param (
[string]$AccessToken
)
$EnvVars = @{}
if (!$NoPrerequisites) {
& "$PSScriptRoot\tools\Install-NuGetCredProvider.ps1" -AccessToken $AccessToken
& "$PSScriptRoot\tools\Install-NuGetCredProvider.ps1" -AccessToken $AccessToken -Force:$UpgradePrerequisites
& "$PSScriptRoot\tools\Install-DotNetSdk.ps1" -InstallLocality $InstallLocality
if ($LASTEXITCODE -eq 3010) {
Exit 3010
}
# The procdump tool and env var is required for dotnet test to collect hang/crash dumps of tests.
# But it only works on Windows.
if ($env:OS -eq 'Windows_NT') {
$EnvVars['PROCDUMP_PATH'] = & "$PSScriptRoot\azure-pipelines\Get-ProcDump.ps1"
}
}
# Workaround nuget credential provider bug that causes very unreliable package restores on Azure Pipelines
@ -52,15 +74,14 @@ Push-Location $PSScriptRoot
try {
$HeaderColor = 'Green'
if (!$NoRestore) {
if (!$NoRestore -and $PSCmdlet.ShouldProcess("NuGet packages", "Restore")) {
Write-Host "Restoring NuGet packages" -ForegroundColor $HeaderColor
dotnet restore src
dotnet restore
if ($lastexitcode -ne 0) {
throw "Failure while restoring packages."
}
}
$EnvVars = @{}
$InstallNuGetPkgScriptPath = ".\azure-pipelines\Install-NuGetPackage.ps1"
$nugetVerbosity = 'quiet'
if ($Verbose) { $nugetVerbosity = 'normal' }
@ -77,7 +98,7 @@ try {
$EnvVars['OptProfEnabled'] = '1'
}
& "$PSScriptRoot\azure-pipelines\Set-EnvVars.ps1" -Variables $EnvVars | Out-Null
& "$PSScriptRoot/tools/Set-EnvVars.ps1" -Variables $EnvVars | Out-Null
}
catch {
Write-Error $error[0]

13
src/.editorconfig Normal file
Просмотреть файл

@ -0,0 +1,13 @@
[Microsoft.VisualStudio.Threading.Analyzers*/*.cs]
# CS1591: Missing XML comment for publicly visible type or member
dotnet_diagnostic.CS1591.severity = suggestion
# CA1062: Validate arguments of public methods
dotnet_diagnostic.CA1062.severity = none
# CA2007: Consider calling ConfigureAwait on the awaited task
dotnet_diagnostic.CA2007.severity = silent
# RS2008: Enable analyzer release tracking
dotnet_diagnostic.RS2008.severity = suggestion

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

@ -1,50 +1,11 @@
<Project>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory)../, Directory.Build.props))\Directory.Build.props" Condition=" '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory)../, Directory.Build.props))' != '' " />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<RepoRoot>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\'))</RepoRoot>
<RepoBinPath>$(RepoRoot)bin\</RepoBinPath>
<RepoObjPath>$(RepoRoot)obj\</RepoObjPath>
<BaseIntermediateOutputPath>$(RepoObjPath)\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
<BaseOutputPath Condition=" '$(BaseOutputPath)' == '' ">$(RepoBinPath)\$(MSBuildProjectName)\</BaseOutputPath>
<PackageOutputPath>$(RepoBinPath)Packages\$(Configuration)\NuGet\</PackageOutputPath>
<UpdateXlfOnBuild Condition=" '$(UpdateXlfOnBuild)' == '' ">true</UpdateXlfOnBuild>
<LangVersion>8</LangVersion>
<Nullable>enable</Nullable>
<MicroBuildVersion>2.0.58</MicroBuildVersion>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<Authors>Microsoft</Authors>
<Owners>Microsoft, VisualStudioExtensibility</Owners>
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
<PackageProjectUrl>https://github.com/Microsoft/vs-threading</PackageProjectUrl>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<PackageIcon>PackageIcon.png</PackageIcon>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>
<ItemGroup Condition=" '$(MSBuildProjectExtension)' != '.vcxproj' ">
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="Nerdbank.GitVersioning" Version="3.2.31" PrivateAssets="all" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.164" PrivateAssets="all" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.Net.Compilers.Toolset" Version="3.7.0" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)PackageIcon.png" Pack="true" PackagePath="" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="$(MSBuildThisFileDirectory)stylecop.json" />
</ItemGroup>
<Target Name="PrepareReleaseNotes" BeforeTargets="GenerateNuspec" DependsOnTargets="GetBuildVersion">
<PropertyGroup>
<PackageReleaseNotes>https://github.com/microsoft/vs-threading/releases/tag/v$(Version)</PackageReleaseNotes>
</PropertyGroup>
</Target>
</Project>

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

@ -1,8 +1,3 @@
<Project>
<PropertyGroup Condition=" '$(IsTestProject)' == 'true' ">
<CoverletOutputFormat>cobertura</CoverletOutputFormat>
<Exclude>[xunit.*]*,[Microsoft.CodeAnalysis*]*</Exclude>
<!-- Ensure we preserve each coverlet output file per target framework: https://github.com/tonerdo/coverlet/issues/177 -->
<CoverletOutput>$(OutputPath)/</CoverletOutput>
</PropertyGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory)../, Directory.Build.targets))\Directory.Build.targets" Condition=" '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory)../, Directory.Build.targets))' != '' " />
</Project>

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

@ -1,16 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net472</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<CodeAnalysisRuleSet>..\Microsoft.VisualStudio.Threading.Tests\Microsoft.VisualStudio.Threading.Tests.ruleset</CodeAnalysisRuleSet>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
</ItemGroup>
</Project>

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

@ -1,8 +1,5 @@
/********************************************************
* *
* © Copyright (C) Microsoft. All rights reserved. *
* *
*********************************************************/
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Resources;

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
@ -38,10 +39,10 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
/// </remarks>
internal static bool ShouldIgnoreContext(SyntaxNodeAnalysisContext context)
{
var namespaceDeclaration = context.Node.FirstAncestorOrSelf<NamespaceDeclarationSyntax>();
if (namespaceDeclaration != null)
NamespaceDeclarationSyntax? namespaceDeclaration = context.Node.FirstAncestorOrSelf<NamespaceDeclarationSyntax>();
if (namespaceDeclaration is object)
{
foreach (var trivia in namespaceDeclaration.NamespaceKeyword.GetAllTrivia())
foreach (SyntaxTrivia trivia in namespaceDeclaration.NamespaceKeyword.GetAllTrivia())
{
const string autoGeneratedKeyword = @"<auto-generated>";
if (trivia.FullSpan.Length > autoGeneratedKeyword.Length
@ -62,12 +63,12 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
IEnumerable<CommonInterest.SyncBlockingMethod> problematicMethods,
bool ignoreIfInsideAnonymousDelegate = false)
{
if (descriptor == null)
if (descriptor is null)
{
throw new ArgumentNullException(nameof(descriptor));
}
if (memberAccessSyntax == null)
if (memberAccessSyntax is null)
{
return;
}
@ -77,7 +78,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
return;
}
if (ignoreIfInsideAnonymousDelegate && context.Node.FirstAncestorOrSelf<AnonymousFunctionExpressionSyntax>() != null)
if (ignoreIfInsideAnonymousDelegate && context.Node.FirstAncestorOrSelf<AnonymousFunctionExpressionSyntax>() is object)
{
// We do not analyze JTF.Run inside anonymous functions because
// they are so often used as callbacks where the signature is constrained.
@ -90,16 +91,16 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
return;
}
var typeReceiver = context.SemanticModel.GetTypeInfo(memberAccessSyntax.Expression).Type;
if (typeReceiver != null)
ITypeSymbol? typeReceiver = context.SemanticModel.GetTypeInfo(memberAccessSyntax.Expression).Type;
if (typeReceiver is object)
{
foreach (var item in problematicMethods)
foreach (CommonInterest.SyncBlockingMethod item in problematicMethods)
{
if (memberAccessSyntax.Name.Identifier.Text == item.Method.Name &&
typeReceiver.Name == item.Method.ContainingType.Name &&
typeReceiver.BelongsToNamespace(item.Method.ContainingType.Namespace))
{
var location = memberAccessSyntax.Name.GetLocation();
Location? location = memberAccessSyntax.Name.GetLocation();
context.ReportDiagnostic(Diagnostic.Create(descriptor, location));
}
}

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
@ -22,7 +23,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
internal static ExpressionSyntax IsolateMethodName(InvocationExpressionSyntax invocation)
{
if (invocation == null)
if (invocation is null)
{
throw new ArgumentNullException(nameof(invocation));
}
@ -39,7 +40,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
/// <returns>The containing function, and metadata for it.</returns>
internal static ContainingFunctionData GetContainingFunction(CSharpSyntaxNode syntaxNode)
{
while (syntaxNode != null)
while (syntaxNode is object)
{
if (syntaxNode is SimpleLambdaExpressionSyntax simpleLambda)
{
@ -75,7 +76,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
internal static bool IsOnLeftHandOfAssignment(SyntaxNode syntaxNode)
{
SyntaxNode? parent = null;
while ((parent = syntaxNode.Parent) != null)
while ((parent = syntaxNode.Parent) is object)
{
if (parent is AssignmentExpressionSyntax assignment)
{
@ -90,26 +91,26 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
internal static bool IsAssignedWithin(SyntaxNode container, SemanticModel semanticModel, ISymbol variable, CancellationToken cancellationToken)
{
if (semanticModel == null)
if (semanticModel is null)
{
throw new ArgumentNullException(nameof(semanticModel));
}
if (variable == null)
if (variable is null)
{
throw new ArgumentNullException(nameof(variable));
}
if (container == null)
if (container is null)
{
return false;
}
foreach (var node in container.DescendantNodesAndSelf(n => !(n is AnonymousFunctionExpressionSyntax)))
foreach (SyntaxNode? node in container.DescendantNodesAndSelf(n => !(n is AnonymousFunctionExpressionSyntax)))
{
if (node is AssignmentExpressionSyntax assignment)
{
var assignedSymbol = semanticModel.GetSymbolInfo(assignment.Left, cancellationToken).Symbol;
ISymbol? assignedSymbol = semanticModel.GetSymbolInfo(assignment.Left, cancellationToken).Symbol;
if (variable.Equals(assignedSymbol))
{
return true;
@ -122,12 +123,12 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
internal static MemberAccessExpressionSyntax MemberAccess(IReadOnlyList<string> qualifiers, SimpleNameSyntax simpleName)
{
if (qualifiers == null)
if (qualifiers is null)
{
throw new ArgumentNullException(nameof(qualifiers));
}
if (simpleName == null)
if (simpleName is null)
{
throw new ArgumentNullException(nameof(simpleName));
}
@ -140,7 +141,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
ExpressionSyntax result = SyntaxFactory.IdentifierName(qualifiers[0]);
for (int i = 1; i < qualifiers.Count; i++)
{
var rightSide = SyntaxFactory.IdentifierName(qualifiers[i]);
IdentifierNameSyntax? rightSide = SyntaxFactory.IdentifierName(qualifiers[i]);
result = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, result, rightSide);
}
@ -152,7 +153,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
/// </summary>
internal static bool IsWithinNameOf([NotNullWhen(true)] SyntaxNode? syntaxNode)
{
var invocation = syntaxNode?.FirstAncestorOrSelf<InvocationExpressionSyntax>();
InvocationExpressionSyntax? invocation = syntaxNode?.FirstAncestorOrSelf<InvocationExpressionSyntax>();
return invocation is object
&& (invocation.Expression as IdentifierNameSyntax)?.Identifier.Text == "nameof"
&& invocation.ArgumentList.Arguments.Count == 1;
@ -160,14 +161,14 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
internal override Location? GetLocationOfBaseTypeName(INamedTypeSymbol symbol, INamedTypeSymbol baseType, Compilation compilation, CancellationToken cancellationToken)
{
foreach (var syntaxReference in symbol.DeclaringSyntaxReferences)
foreach (SyntaxReference? syntaxReference in symbol.DeclaringSyntaxReferences)
{
var syntaxNode = syntaxReference.GetSyntax(cancellationToken);
SyntaxNode? syntaxNode = syntaxReference.GetSyntax(cancellationToken);
if (syntaxNode is TypeDeclarationSyntax typeDeclarationSyntax)
{
if (compilation.GetSemanticModel(typeDeclarationSyntax.SyntaxTree) is { } semanticModel)
{
foreach (var baseTypeSyntax in typeDeclarationSyntax.BaseList.Types)
foreach (BaseTypeSyntax? baseTypeSyntax in typeDeclarationSyntax.BaseList.Types)
{
SymbolInfo baseTypeSymbolInfo = semanticModel.GetSymbolInfo(baseTypeSyntax.Type, cancellationToken);
if (Equals(baseTypeSymbolInfo.Symbol, baseType))

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{

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

@ -1,9 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<CodeAnalysisRuleSet>..\Microsoft.VisualStudio.Threading.Analyzers\Microsoft.VisualStudio.Threading.Analyzers.ruleset</CodeAnalysisRuleSet>
<RootNamespace>Microsoft.VisualStudio.Threading.Analyzers</RootNamespace>
<IsPackable>false</IsPackable>

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

@ -1,8 +1,5 @@
/********************************************************
* *
* © Copyright (C) Microsoft. All rights reserved. *
* *
*********************************************************/
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
@ -63,18 +60,18 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
context.RegisterCompilationStartAction(compilationContext =>
{
var taskSymbol = compilationContext.Compilation.GetTypeByMetadataName(Types.Task.FullName);
if (taskSymbol != null)
INamedTypeSymbol? taskSymbol = compilationContext.Compilation.GetTypeByMetadataName(Types.Task.FullName);
if (taskSymbol is object)
{
compilationContext.RegisterCodeBlockStartAction<SyntaxKind>(codeBlockContext =>
{
// We want to scan properties and methods that do not return Task or Task<T>.
var methodSymbol = codeBlockContext.OwningSymbol as IMethodSymbol;
var propertySymbol = codeBlockContext.OwningSymbol as IPropertySymbol;
if (propertySymbol != null || (methodSymbol != null && !methodSymbol.HasAsyncCompatibleReturnType()))
if (propertySymbol is object || (methodSymbol is object && !methodSymbol.HasAsyncCompatibleReturnType()))
{
codeBlockContext.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(c => this.AnalyzeInvocation(c, taskSymbol)), SyntaxKind.InvocationExpression);
codeBlockContext.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(c => this.AnalyzeMemberAccess(c, taskSymbol)), SyntaxKind.SimpleMemberAccessExpression);
codeBlockContext.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(c => AnalyzeInvocation(c, taskSymbol)), SyntaxKind.InvocationExpression);
codeBlockContext.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(c => AnalyzeMemberAccess(c, taskSymbol)), SyntaxKind.SimpleMemberAccessExpression);
}
});
}
@ -96,43 +93,23 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
return null;
}
private void AnalyzeInvocation(SyntaxNodeAnalysisContext context, INamedTypeSymbol taskSymbol)
{
var invocationExpressionSyntax = (InvocationExpressionSyntax)context.Node;
this.InspectMemberAccess(
context,
invocationExpressionSyntax.Expression as MemberAccessExpressionSyntax,
CommonInterest.ProblematicSyncBlockingMethods,
taskSymbol);
}
private void AnalyzeMemberAccess(SyntaxNodeAnalysisContext context, INamedTypeSymbol taskSymbol)
{
var memberAccessSyntax = (MemberAccessExpressionSyntax)context.Node;
this.InspectMemberAccess(
context,
memberAccessSyntax,
CommonInterest.SyncBlockingProperties,
taskSymbol);
}
private void InspectMemberAccess(
private static void InspectMemberAccess(
SyntaxNodeAnalysisContext context,
MemberAccessExpressionSyntax? memberAccessSyntax,
IEnumerable<CommonInterest.SyncBlockingMethod> problematicMethods,
INamedTypeSymbol taskSymbol)
{
if (memberAccessSyntax == null)
if (memberAccessSyntax is null)
{
return;
}
// Are we in the context of an anonymous function that is passed directly in as an argument to another method?
var anonymousFunctionSyntax = context.Node.FirstAncestorOrSelf<AnonymousFunctionExpressionSyntax>();
AnonymousFunctionExpressionSyntax? anonymousFunctionSyntax = context.Node.FirstAncestorOrSelf<AnonymousFunctionExpressionSyntax>();
var anonFuncAsArgument = anonymousFunctionSyntax?.Parent as ArgumentSyntax;
var invocationPassingExpression = anonFuncAsArgument?.Parent?.Parent as InvocationExpressionSyntax;
var invokedMemberAccess = invocationPassingExpression?.Expression as MemberAccessExpressionSyntax;
if (invokedMemberAccess?.Name != null)
if (invokedMemberAccess?.Name is object)
{
// Does the anonymous function appear as the first argument to Task.ContinueWith?
var invokedMemberSymbol = context.SemanticModel.GetSymbolInfo(invokedMemberAccess.Name, context.CancellationToken).Symbol as IMethodSymbol;
@ -141,8 +118,8 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
invocationPassingExpression?.ArgumentList?.Arguments.FirstOrDefault() == anonFuncAsArgument)
{
// Does the member access being analyzed belong to the Task that just completed?
var firstParameter = GetFirstParameter(anonymousFunctionSyntax);
if (firstParameter != null)
ParameterSyntax? firstParameter = GetFirstParameter(anonymousFunctionSyntax);
if (firstParameter is object)
{
// Are we accessing a member of the completed task?
ISymbol invokedObjectSymbol = context.SemanticModel.GetSymbolInfo(memberAccessSyntax.Expression, context.CancellationToken).Symbol;
@ -158,5 +135,25 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
CSharpCommonInterest.InspectMemberAccess(context, memberAccessSyntax, Descriptor, problematicMethods);
}
private static void AnalyzeInvocation(SyntaxNodeAnalysisContext context, INamedTypeSymbol taskSymbol)
{
var invocationExpressionSyntax = (InvocationExpressionSyntax)context.Node;
InspectMemberAccess(
context,
invocationExpressionSyntax.Expression as MemberAccessExpressionSyntax,
CommonInterest.ProblematicSyncBlockingMethods,
taskSymbol);
}
private static void AnalyzeMemberAccess(SyntaxNodeAnalysisContext context, INamedTypeSymbol taskSymbol)
{
var memberAccessSyntax = (MemberAccessExpressionSyntax)context.Node;
InspectMemberAccess(
context,
memberAccessSyntax,
CommonInterest.SyncBlockingProperties,
taskSymbol);
}
}
}

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

@ -1,8 +1,5 @@
/********************************************************
* *
* © Copyright (C) Microsoft. All rights reserved. *
* *
*********************************************************/
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
@ -52,11 +49,6 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);
private static readonly IReadOnlyCollection<Type> DoNotPassTypesInSearchForAnonFuncInvocation = new[]
{
typeof(MethodDeclarationSyntax),
};
/// <inheritdoc />
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
{
@ -84,7 +76,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
var arrowExpressionClause = (ArrowExpressionClauseSyntax)context.Node;
if (arrowExpressionClause.Parent is MethodDeclarationSyntax)
{
var diagnostic = this.AnalyzeAwaitedOrReturnedExpression(arrowExpressionClause.Expression, context, context.CancellationToken);
Diagnostic? diagnostic = this.AnalyzeAwaitedOrReturnedExpression(arrowExpressionClause.Expression, context, context.CancellationToken);
if (diagnostic is object)
{
context.ReportDiagnostic(diagnostic);
@ -97,8 +89,8 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
var lambdaExpression = (LambdaExpressionSyntax)context.Node;
if (lambdaExpression.Body is ExpressionSyntax expression)
{
var diagnostic = this.AnalyzeAwaitedOrReturnedExpression(expression, context, context.CancellationToken);
if (diagnostic != null)
Diagnostic? diagnostic = this.AnalyzeAwaitedOrReturnedExpression(expression, context, context.CancellationToken);
if (diagnostic is object)
{
context.ReportDiagnostic(diagnostic);
}
@ -108,8 +100,8 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
private void AnalyzeReturnStatement(SyntaxNodeAnalysisContext context)
{
var returnStatement = (ReturnStatementSyntax)context.Node;
var diagnostic = this.AnalyzeAwaitedOrReturnedExpression(returnStatement.Expression, context, context.CancellationToken);
if (diagnostic != null)
Diagnostic? diagnostic = this.AnalyzeAwaitedOrReturnedExpression(returnStatement.Expression, context, context.CancellationToken);
if (diagnostic is object)
{
context.ReportDiagnostic(diagnostic);
}
@ -118,8 +110,8 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
private void AnalyzeAwaitExpression(SyntaxNodeAnalysisContext context)
{
AwaitExpressionSyntax awaitExpressionSyntax = (AwaitExpressionSyntax)context.Node;
var diagnostic = this.AnalyzeAwaitedOrReturnedExpression(awaitExpressionSyntax.Expression, context, context.CancellationToken);
if (diagnostic != null)
Diagnostic? diagnostic = this.AnalyzeAwaitedOrReturnedExpression(awaitExpressionSyntax.Expression, context, context.CancellationToken);
if (diagnostic is object)
{
context.ReportDiagnostic(diagnostic);
}
@ -127,14 +119,14 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
private Diagnostic? AnalyzeAwaitedOrReturnedExpression(ExpressionSyntax expressionSyntax, SyntaxNodeAnalysisContext context, CancellationToken cancellationToken)
{
if (expressionSyntax == null)
if (expressionSyntax is null)
{
return null;
}
// Get the semantic model for the SyntaxTree for the given ExpressionSyntax, since it *may* not be in the same syntax tree
// as the original context.Node.
if (!context.TryGetNewOrExistingSemanticModel(expressionSyntax.SyntaxTree, out var semanticModel))
if (!context.TryGetNewOrExistingSemanticModel(expressionSyntax.SyntaxTree, out SemanticModel? semanticModel))
{
return null;
}
@ -174,12 +166,12 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
return null;
}
foreach (var syntaxReference in fieldSymbol.DeclaringSyntaxReferences)
foreach (SyntaxReference? syntaxReference in fieldSymbol.DeclaringSyntaxReferences)
{
if (syntaxReference.GetSyntax(cancellationToken) is VariableDeclaratorSyntax declarationSyntax)
{
if (declarationSyntax.Initializer?.Value is InvocationExpressionSyntax invocationSyntax &&
invocationSyntax.Expression != null)
invocationSyntax.Expression is object)
{
if (!context.Compilation.ContainsSyntaxTree(invocationSyntax.SyntaxTree))
{
@ -188,7 +180,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
}
// Whitelist Task.From*() methods.
if (!context.TryGetNewOrExistingSemanticModel(invocationSyntax.SyntaxTree, out var declarationSemanticModel))
if (!context.TryGetNewOrExistingSemanticModel(invocationSyntax.SyntaxTree, out SemanticModel? declarationSemanticModel))
{
return null;
}
@ -203,12 +195,12 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
}
else if (declarationSyntax.Initializer?.Value is MemberAccessExpressionSyntax memberAccessSyntax && memberAccessSyntax.Expression is object)
{
if (!context.TryGetNewOrExistingSemanticModel(memberAccessSyntax.SyntaxTree, out var declarationSemanticModel))
if (!context.TryGetNewOrExistingSemanticModel(memberAccessSyntax.SyntaxTree, out SemanticModel? declarationSemanticModel))
{
return null;
}
var definition = declarationSemanticModel.GetSymbolInfo(memberAccessSyntax, cancellationToken).Symbol;
ISymbol? definition = declarationSemanticModel.GetSymbolInfo(memberAccessSyntax, cancellationToken).Symbol;
if (definition is IFieldSymbol field)
{
// Whitelist the TplExtensions.CompletedTask and related fields.
@ -237,7 +229,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
if (Utils.IsTask(methodSymbol.ReturnType) && expressionSyntax is InvocationExpressionSyntax invocationExpressionSyntax)
{
// Consider all arguments
var expressionsToConsider = invocationExpressionSyntax.ArgumentList.Arguments.Select(a => a.Expression);
IEnumerable<ExpressionSyntax>? expressionsToConsider = invocationExpressionSyntax.ArgumentList.Arguments.Select(a => a.Expression);
// Consider the implicit first argument when this method is invoked as an extension method.
if (methodSymbol.IsExtensionMethod && invocationExpressionSyntax.Expression is MemberAccessExpressionSyntax invokedMember)
@ -248,7 +240,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
}
}
return expressionsToConsider.Select(e => this.AnalyzeAwaitedOrReturnedExpression(e, context, cancellationToken)).FirstOrDefault(r => r != null);
return expressionsToConsider.Select(e => this.AnalyzeAwaitedOrReturnedExpression(e, context, cancellationToken)).FirstOrDefault(r => r is object);
}
return null;
@ -262,7 +254,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
}
// Report warning if the task was not initialized within the current delegate or lambda expression
var containingFunc = CSharpUtils.GetContainingFunction(expressionSyntax);
CSharpUtils.ContainingFunctionData containingFunc = CSharpUtils.GetContainingFunction(expressionSyntax);
if (containingFunc.BlockOrExpression is BlockSyntax delegateBlock)
{
if (dataflowAnalysisCompatibleVariable)

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

@ -1,4 +1,7 @@
namespace Microsoft.VisualStudio.Threading.Analyzers
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
using System;
using System.Collections.Generic;
@ -114,7 +117,7 @@
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();
var 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));
@ -145,9 +148,9 @@
compilationStartContext.RegisterCompilationEndAction(compilationEndContext =>
{
var calleeToCallerMap = CreateCalleeToCallerMap(callerToCalleeMap);
var transitiveClosureOfMainThreadRequiringMethods = GetTransitiveClosureOfMainThreadRequiringMethods(methodsAssertingUIThreadRequirement, calleeToCallerMap);
foreach (var implicitUserMethod in transitiveClosureOfMainThreadRequiringMethods.Except(methodsDeclaringUIThreadRequirement))
Dictionary<IMethodSymbol, List<CallInfo>>? calleeToCallerMap = CreateCalleeToCallerMap(callerToCalleeMap);
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)
@ -156,7 +159,7 @@
foreach (var site in reportSites)
{
bool isAsync = Utils.IsAsyncReady(implicitUserMethod);
var descriptor = isAsync ? DescriptorAsync : DescriptorSync;
DiagnosticDescriptor? descriptor = isAsync ? DescriptorAsync : DescriptorSync;
string calleeName = Utils.GetFullName(site.CalleeMethod);
var formattingArgs = isAsync ? new object[] { calleeName } : new object[] { calleeName, mainThreadAssertingMethods.FirstOrDefault() };
Diagnostic diagnostic = Diagnostic.Create(
@ -177,12 +180,12 @@
void MarkMethod(IMethodSymbol method)
{
if (result.Add(method) && calleeToCallerMap.TryGetValue(method, out var callers))
if (result.Add(method) && calleeToCallerMap.TryGetValue(method, out List<CallInfo>? callers))
{
// If this is an async method, do *not* propagate its thread affinity to its callers.
if (!Utils.IsAsyncCompatibleReturnType(method.ReturnType))
{
foreach (var caller in callers)
foreach (CallInfo caller in callers)
{
MarkMethod(caller.MethodSymbol);
}
@ -190,7 +193,7 @@
}
}
foreach (var method in methodsRequiringUIThread)
foreach (IMethodSymbol? method in methodsRequiringUIThread)
{
MarkMethod(method);
}
@ -207,7 +210,7 @@
IMethodSymbol? GetPropertyAccessor(IPropertySymbol? propertySymbol)
{
if (propertySymbol != null)
if (propertySymbol is object)
{
return CSharpUtils.IsOnLeftHandOfAssignment(context.Node)
? propertySymbol.SetMethod
@ -251,12 +254,12 @@
{
var result = new Dictionary<IMethodSymbol, List<CallInfo>>();
foreach (var item in callerToCalleeMap)
foreach (KeyValuePair<IMethodSymbol, List<CallInfo>> item in callerToCalleeMap)
{
var caller = item.Key;
foreach (var callee in item.Value)
IMethodSymbol? caller = item.Key;
foreach (CallInfo callee in item.Value)
{
if (!result.TryGetValue(callee.MethodSymbol, out var callers))
if (!result.TryGetValue(callee.MethodSymbol, out List<CallInfo>? callers))
{
result[callee.MethodSymbol] = callers = new List<CallInfo>();
}
@ -317,10 +320,10 @@
{
var invocationSyntax = (InvocationExpressionSyntax)context.Node;
var invokedMethod = context.SemanticModel.GetSymbolInfo(context.Node).Symbol as IMethodSymbol;
if (invokedMethod != null)
if (invokedMethod is object)
{
var methodDeclaration = context.Node.FirstAncestorOrSelf<SyntaxNode>(n => CSharpCommonInterest.MethodSyntaxKinds.Contains(n.Kind()));
if (methodDeclaration != null)
SyntaxNode? methodDeclaration = context.Node.FirstAncestorOrSelf<SyntaxNode>(n => CSharpCommonInterest.MethodSyntaxKinds.Contains(n.Kind()));
if (methodDeclaration is object)
{
bool assertsMainThread = this.MainThreadAssertingMethods.Contains(invokedMethod);
bool switchesToMainThread = this.MainThreadSwitchingMethods.Contains(invokedMethod);
@ -348,11 +351,11 @@
}
// The diagnostic (if any) should underline the method name only.
var focusedNode = invocationSyntax.Expression;
ExpressionSyntax? focusedNode = invocationSyntax.Expression;
focusedNode = (focusedNode as MemberAccessExpressionSyntax)?.Name ?? focusedNode;
if (!this.AnalyzeMemberWithinContext(invokedMethod.ContainingType, invokedMethod, context, focusedNode.GetLocation()))
{
foreach (var iface in invokedMethod.FindInterfacesImplemented())
foreach (ITypeSymbol? iface in invokedMethod.FindInterfacesImplemented())
{
if (this.AnalyzeMemberWithinContext(iface, invokedMethod, context, focusedNode.GetLocation()))
{
@ -368,7 +371,7 @@
{
var memberAccessSyntax = (MemberAccessExpressionSyntax)context.Node;
var property = context.SemanticModel.GetSymbolInfo(context.Node).Symbol as IPropertySymbol;
if (property != null)
if (property is object)
{
this.AnalyzeMemberWithinContext(property.ContainingType, property, context, memberAccessSyntax.Name.GetLocation());
}
@ -378,7 +381,7 @@
{
var castSyntax = (CastExpressionSyntax)context.Node;
var type = context.SemanticModel.GetSymbolInfo(castSyntax.Type, context.CancellationToken).Symbol as ITypeSymbol;
if (type != null && IsObjectLikelyToBeCOMObject(type))
if (type is object && IsObjectLikelyToBeCOMObject(type))
{
this.AnalyzeMemberWithinContext(type, null, context);
}
@ -388,7 +391,7 @@
{
var asSyntax = (BinaryExpressionSyntax)context.Node;
var type = context.SemanticModel.GetSymbolInfo(asSyntax.Right, context.CancellationToken).Symbol as ITypeSymbol;
if (type != null && IsObjectLikelyToBeCOMObject(type))
if (type is object && IsObjectLikelyToBeCOMObject(type))
{
Location asAndRightSide = Location.Create(context.Node.SyntaxTree, TextSpan.FromBounds(asSyntax.OperatorToken.Span.Start, asSyntax.Right.Span.End));
this.AnalyzeMemberWithinContext(type, null, context, asAndRightSide);
@ -398,15 +401,16 @@
internal void AnalyzeIsPattern(SyntaxNodeAnalysisContext context)
{
var patternSyntax = (IsPatternExpressionSyntax)context.Node;
if (patternSyntax.Pattern is DeclarationPatternSyntax declarationPatternSyntax && declarationPatternSyntax.Type != null)
if (patternSyntax.Pattern is DeclarationPatternSyntax declarationPatternSyntax && declarationPatternSyntax.Type is object)
{
var type = context.SemanticModel.GetSymbolInfo(declarationPatternSyntax.Type, context.CancellationToken).Symbol as ITypeSymbol;
if (type != null && IsObjectLikelyToBeCOMObject(type))
if (type is object && IsObjectLikelyToBeCOMObject(type))
{
Location isAndTypeSide = Location.Create(
context.Node.SyntaxTree,
TextSpan.FromBounds(patternSyntax.IsKeyword.SpanStart,
declarationPatternSyntax.Type.Span.End));
TextSpan.FromBounds(
patternSyntax.IsKeyword.SpanStart,
declarationPatternSyntax.Type.Span.End));
this.AnalyzeMemberWithinContext(type, null, context, isAndTypeSide);
}
}
@ -421,7 +425,7 @@
/// </remarks>
private static bool IsObjectLikelyToBeCOMObject(ITypeSymbol typeSymbol)
{
if (typeSymbol == null)
if (typeSymbol is null)
{
throw new ArgumentNullException(nameof(typeSymbol));
}
@ -435,7 +439,7 @@
private bool AnalyzeMemberWithinContext(ITypeSymbol type, ISymbol? symbol, SyntaxNodeAnalysisContext context, Location? focusDiagnosticOn = null)
{
if (type == null)
if (type is null)
{
throw new ArgumentNullException(nameof(type));
}
@ -445,18 +449,18 @@
if (requiresUIThread)
{
var threadingContext = ThreadingContext.Unknown;
var methodDeclaration = context.Node.FirstAncestorOrSelf<SyntaxNode>(n => CSharpCommonInterest.MethodSyntaxKinds.Contains(n.Kind()));
if (methodDeclaration != null)
ThreadingContext threadingContext = ThreadingContext.Unknown;
SyntaxNode? methodDeclaration = context.Node.FirstAncestorOrSelf<SyntaxNode>(n => CSharpCommonInterest.MethodSyntaxKinds.Contains(n.Kind()));
if (methodDeclaration is object)
{
threadingContext = this.methodDeclarationNodes.GetValueOrDefault(methodDeclaration);
}
if (threadingContext != ThreadingContext.MainThread)
{
var function = CSharpUtils.GetContainingFunction((CSharpSyntaxNode)context.Node);
CSharpUtils.ContainingFunctionData function = CSharpUtils.GetContainingFunction((CSharpSyntaxNode)context.Node);
Location location = focusDiagnosticOn ?? context.Node.GetLocation();
var descriptor = function.IsAsync ? DescriptorAsync : DescriptorSync;
DiagnosticDescriptor? descriptor = function.IsAsync ? DescriptorAsync : DescriptorSync;
var formattingArgs = function.IsAsync ? new object[] { type.Name } : new object[] { type.Name, this.MainThreadAssertingMethods.FirstOrDefault() };
context.ReportDiagnostic(Diagnostic.Create(descriptor, location, this.DiagnosticProperties, formattingArgs));
return true;

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

@ -1,4 +1,7 @@
namespace Microsoft.VisualStudio.Threading.Analyzers
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
using System;
using System.Collections.Generic;
@ -48,15 +51,14 @@
var methodSymbol = ctxt.OwningSymbol as IMethodSymbol;
if (!methodSymbol.HasAsyncCompatibleReturnType() && !Utils.IsPublic(methodSymbol) && !Utils.IsEntrypointMethod(methodSymbol, ctxt.SemanticModel, ctxt.CancellationToken) && !methodSymbol.FindInterfacesImplemented().Any(Utils.IsPublic))
{
var methodAnalyzer = new MethodAnalyzer();
ctxt.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(methodAnalyzer.AnalyzeInvocation), SyntaxKind.InvocationExpression);
ctxt.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(MethodAnalyzer.AnalyzeInvocation), SyntaxKind.InvocationExpression);
}
});
}
private class MethodAnalyzer
private static class MethodAnalyzer
{
internal void AnalyzeInvocation(SyntaxNodeAnalysisContext context)
internal static void AnalyzeInvocation(SyntaxNodeAnalysisContext context)
{
var invocationExpressionSyntax = (InvocationExpressionSyntax)context.Node;
CSharpCommonInterest.InspectMemberAccess(

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

@ -1,4 +1,7 @@
namespace Microsoft.VisualStudio.Threading.Analyzers
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
using System;
using System.Collections.Generic;
@ -67,15 +70,14 @@
context.RegisterCodeBlockStartAction<SyntaxKind>(ctxt =>
{
var methodAnalyzer = new MethodAnalyzer();
ctxt.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(methodAnalyzer.AnalyzeInvocation), SyntaxKind.InvocationExpression);
ctxt.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(methodAnalyzer.AnalyzePropertyGetter), SyntaxKind.SimpleMemberAccessExpression);
ctxt.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(MethodAnalyzer.AnalyzeInvocation), SyntaxKind.InvocationExpression);
ctxt.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(MethodAnalyzer.AnalyzePropertyGetter), SyntaxKind.SimpleMemberAccessExpression);
});
}
private class MethodAnalyzer
{
internal void AnalyzePropertyGetter(SyntaxNodeAnalysisContext context)
internal static void AnalyzePropertyGetter(SyntaxNodeAnalysisContext context)
{
var memberAccessSyntax = (MemberAccessExpressionSyntax)context.Node;
if (IsInTaskReturningMethodOrDelegate(context))
@ -84,7 +86,7 @@
}
}
internal void AnalyzeInvocation(SyntaxNodeAnalysisContext context)
internal static void AnalyzeInvocation(SyntaxNodeAnalysisContext context)
{
if (IsInTaskReturningMethodOrDelegate(context))
{
@ -100,19 +102,19 @@
// Also consider all method calls to check for Async-suffixed alternatives.
ExpressionSyntax invokedMethodName = CSharpUtils.IsolateMethodName(invocationExpressionSyntax);
var symbolInfo = context.SemanticModel.GetSymbolInfo(invocationExpressionSyntax, context.CancellationToken);
SymbolInfo symbolInfo = context.SemanticModel.GetSymbolInfo(invocationExpressionSyntax, context.CancellationToken);
var methodSymbol = symbolInfo.Symbol as IMethodSymbol;
if (methodSymbol != null && !methodSymbol.Name.EndsWith(VSTHRD200UseAsyncNamingConventionAnalyzer.MandatoryAsyncSuffix) &&
if (methodSymbol is object && !methodSymbol.Name.EndsWith(VSTHRD200UseAsyncNamingConventionAnalyzer.MandatoryAsyncSuffix, StringComparison.CurrentCulture) &&
!(methodSymbol.ReturnType?.Name == nameof(Task) && methodSymbol.ReturnType.BelongsToNamespace(Namespaces.SystemThreadingTasks)))
{
string asyncMethodName = methodSymbol.Name + VSTHRD200UseAsyncNamingConventionAnalyzer.MandatoryAsyncSuffix;
var symbols = context.SemanticModel.LookupSymbols(
ImmutableArray<ISymbol> symbols = context.SemanticModel.LookupSymbols(
invocationExpressionSyntax.Expression.GetLocation().SourceSpan.Start,
methodSymbol.ContainingType,
asyncMethodName,
includeReducedExtensionMethods: true);
foreach (var s in symbols)
foreach (ISymbol? s in symbols)
{
if (s is IMethodSymbol m
&& !m.IsObsolete()
@ -121,7 +123,7 @@
&& Utils.HasAsyncCompatibleReturnType(m))
{
// An async alternative exists.
var properties = ImmutableDictionary<string, string>.Empty
ImmutableDictionary<string, string>? properties = ImmutableDictionary<string, string>.Empty
.Add(AsyncMethodKeyName, asyncMethodName);
Diagnostic diagnostic = Diagnostic.Create(
@ -157,47 +159,47 @@
// We want to scan invocations that occur inside Task and Task<T>-returning delegates or methods.
// That is: methods that either are or could be made async.
IMethodSymbol? methodSymbol = null;
var anonymousFunc = context.Node.FirstAncestorOrSelf<AnonymousFunctionExpressionSyntax>();
if (anonymousFunc != null)
AnonymousFunctionExpressionSyntax? anonymousFunc = context.Node.FirstAncestorOrSelf<AnonymousFunctionExpressionSyntax>();
if (anonymousFunc is object)
{
var symbolInfo = context.SemanticModel.GetSymbolInfo(anonymousFunc, context.CancellationToken);
SymbolInfo symbolInfo = context.SemanticModel.GetSymbolInfo(anonymousFunc, context.CancellationToken);
methodSymbol = symbolInfo.Symbol as IMethodSymbol;
}
else
{
var methodDecl = context.Node.FirstAncestorOrSelf<MethodDeclarationSyntax>();
if (methodDecl != null)
MethodDeclarationSyntax? methodDecl = context.Node.FirstAncestorOrSelf<MethodDeclarationSyntax>();
if (methodDecl is object)
{
methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodDecl, context.CancellationToken);
}
}
var returnType = methodSymbol?.ReturnType;
ITypeSymbol? returnType = methodSymbol?.ReturnType;
return returnType?.Name == nameof(Task)
&& returnType.BelongsToNamespace(Namespaces.SystemThreadingTasks);
}
private static bool InspectMemberAccess(SyntaxNodeAnalysisContext context, [NotNullWhen(true)] MemberAccessExpressionSyntax? memberAccessSyntax, IEnumerable<CommonInterest.SyncBlockingMethod> problematicMethods)
{
if (memberAccessSyntax == null)
if (memberAccessSyntax is null)
{
return false;
}
var memberSymbol = context.SemanticModel.GetSymbolInfo(memberAccessSyntax, context.CancellationToken).Symbol;
if (memberSymbol != null)
ISymbol? memberSymbol = context.SemanticModel.GetSymbolInfo(memberAccessSyntax, context.CancellationToken).Symbol;
if (memberSymbol is object)
{
foreach (var item in problematicMethods)
foreach (CommonInterest.SyncBlockingMethod item in problematicMethods)
{
if (item.Method.IsMatch(memberSymbol))
{
var location = memberAccessSyntax.Name.GetLocation();
var properties = ImmutableDictionary<string, string>.Empty
.Add(ExtensionMethodNamespaceKeyName, item.ExtensionMethodNamespace != null ? string.Join(".", item.ExtensionMethodNamespace) : string.Empty);
Location? location = memberAccessSyntax.Name.GetLocation();
ImmutableDictionary<string, string>? properties = ImmutableDictionary<string, string>.Empty
.Add(ExtensionMethodNamespaceKeyName, item.ExtensionMethodNamespace is object ? string.Join(".", item.ExtensionMethodNamespace) : string.Empty);
DiagnosticDescriptor descriptor;
var messageArgs = new List<object>(2);
messageArgs.Add(item.Method.Name);
if (item.AsyncAlternativeMethodName != null)
if (item.AsyncAlternativeMethodName is object)
{
properties = properties.Add(AsyncMethodKeyName, item.AsyncAlternativeMethodName);
descriptor = Descriptor;

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

@ -1,4 +1,7 @@
namespace Microsoft.VisualStudio.Threading.Analyzers
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
using System;
using System.Collections.Generic;
@ -64,7 +67,7 @@
private void InspectMemberAccess(SyntaxNodeAnalysisContext context, MemberAccessExpressionSyntax? memberAccessSyntax, IEnumerable<CommonInterest.SyncBlockingMethod> problematicMethods)
{
if (memberAccessSyntax == null)
if (memberAccessSyntax is null)
{
return;
}
@ -75,7 +78,7 @@
return;
}
if (context.Node.FirstAncestorOrSelf<AnonymousFunctionExpressionSyntax>() != null)
if (context.Node.FirstAncestorOrSelf<AnonymousFunctionExpressionSyntax>() is object)
{
// We do not analyze JTF.Run inside anonymous functions because
// they are so often used as callbacks where the signature is constrained.
@ -88,14 +91,14 @@
return;
}
var invokedMember = context.SemanticModel.GetSymbolInfo(memberAccessSyntax, context.CancellationToken).Symbol;
if (invokedMember != null)
ISymbol? invokedMember = context.SemanticModel.GetSymbolInfo(memberAccessSyntax, context.CancellationToken).Symbol;
if (invokedMember is object)
{
foreach (var item in problematicMethods)
foreach (CommonInterest.SyncBlockingMethod item in problematicMethods)
{
if (item.Method.IsMatch(invokedMember))
{
var location = memberAccessSyntax.Name.GetLocation();
Location? location = memberAccessSyntax.Name.GetLocation();
context.ReportDiagnostic(Diagnostic.Create(Descriptor, location));
this.diagnosticReported = true;
}

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

@ -1,8 +1,5 @@
/********************************************************
* *
* © Copyright (C) Microsoft. All rights reserved. *
* *
*********************************************************/
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
@ -63,7 +60,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
private void AnalyzeNode(SyntaxNodeAnalysisContext context)
{
var usingStatement = (UsingStatementSyntax)context.Node;
if (usingStatement.Expression != null)
if (usingStatement.Expression is object)
{
TypeInfo expressionTypeInfo = context.SemanticModel.GetTypeInfo(usingStatement.Expression, context.CancellationToken);
ITypeSymbol expressionType = expressionTypeInfo.Type;

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

@ -1,4 +1,7 @@
namespace Microsoft.VisualStudio.Threading.Analyzers
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
using System;
using System.Collections.Generic;
@ -48,12 +51,12 @@
if (invocation.Parent?.GetType().Equals(typeof(ExpressionStatementSyntax)) ?? false)
{
var methodSymbol = context.SemanticModel.GetSymbolInfo(context.Node).Symbol as IMethodSymbol;
var returnedSymbol = methodSymbol?.ReturnType;
ITypeSymbol? returnedSymbol = methodSymbol?.ReturnType;
if (returnedSymbol?.Name == Types.Task.TypeName && returnedSymbol.BelongsToNamespace(Types.Task.Namespace))
{
if (!CSharpUtils.GetContainingFunction(invocation).IsAsync)
{
var location = (CSharpUtils.IsolateMethodName(invocation) ?? invocation.Expression).GetLocation();
Location? location = (CSharpUtils.IsolateMethodName(invocation) ?? invocation.Expression).GetLocation();
context.ReportDiagnostic(Diagnostic.Create(Descriptor, location));
}
}

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

@ -1,8 +1,5 @@
/********************************************************
* *
* © Copyright (C) Microsoft. All rights reserved. *
* *
*********************************************************/
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Resources;

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

@ -1,4 +1,7 @@
namespace Microsoft.VisualStudio.Threading.Analyzers
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
using System;
using System.Collections.Generic;
@ -22,7 +25,7 @@
{
internal static async Task<ImmutableArray<QualifiedMember>> ReadMethodsAsync(CodeFixContext codeFixContext, Regex fileNamePattern, CancellationToken cancellationToken)
{
var result = ImmutableArray.CreateBuilder<QualifiedMember>();
ImmutableArray<QualifiedMember>.Builder? result = ImmutableArray.CreateBuilder<QualifiedMember>();
foreach (string line in await ReadAdditionalFilesAsync(codeFixContext.Document.Project.AdditionalDocuments, fileNamePattern, cancellationToken))
{
result.Add(ParseAdditionalFileMethodLine(line));
@ -33,24 +36,24 @@
internal static async Task<ImmutableArray<string>> ReadAdditionalFilesAsync(IEnumerable<TextDocument> additionalFiles, Regex fileNamePattern, CancellationToken cancellationToken)
{
if (additionalFiles == null)
if (additionalFiles is null)
{
throw new ArgumentNullException(nameof(additionalFiles));
}
if (fileNamePattern == null)
if (fileNamePattern is null)
{
throw new ArgumentNullException(nameof(fileNamePattern));
}
var docs = from doc in additionalFiles.OrderBy(x => x.FilePath, StringComparer.Ordinal)
IEnumerable<TextDocument>? docs = from doc in additionalFiles.OrderBy(x => x.FilePath, StringComparer.Ordinal)
let fileName = Path.GetFileName(doc.Name)
where fileNamePattern.IsMatch(fileName)
select doc;
var result = ImmutableArray.CreateBuilder<string>();
foreach (var doc in docs)
ImmutableArray<string>.Builder? result = ImmutableArray.CreateBuilder<string>();
foreach (TextDocument? doc in docs)
{
var text = await doc.GetTextAsync(cancellationToken);
SourceText? text = await doc.GetTextAsync(cancellationToken);
result.AddRange(ReadLinesFromAdditionalFile(text));
}

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

@ -1,8 +1,5 @@
/********************************************************
* *
* © Copyright (C) Microsoft. All rights reserved. *
* *
*********************************************************/
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
@ -20,7 +17,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
internal static class FixUtils
{
internal static readonly string BookmarkAnnotationName = "Bookmark";
internal const string BookmarkAnnotationName = "Bookmark";
internal static AnonymousFunctionExpressionSyntax MakeMethodAsync(this AnonymousFunctionExpressionSyntax method, bool hasReturnValue, SemanticModel semanticModel, CancellationToken cancellationToken)
{
@ -33,7 +30,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
AnonymousFunctionExpressionSyntax? updated = null;
var simpleLambda = method as SimpleLambdaExpressionSyntax;
if (simpleLambda != null)
if (simpleLambda is object)
{
updated = simpleLambda
.WithAsyncKeyword(SyntaxFactory.Token(SyntaxKind.AsyncKeyword))
@ -41,7 +38,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
}
var parentheticalLambda = method as ParenthesizedLambdaExpressionSyntax;
if (parentheticalLambda != null)
if (parentheticalLambda is object)
{
updated = parentheticalLambda
.WithAsyncKeyword(SyntaxFactory.Token(SyntaxKind.AsyncKeyword))
@ -49,14 +46,14 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
}
var anonymousMethod = method as AnonymousMethodExpressionSyntax;
if (anonymousMethod != null)
if (anonymousMethod is object)
{
updated = anonymousMethod
.WithAsyncKeyword(SyntaxFactory.Token(SyntaxKind.AsyncKeyword))
.WithBody(UpdateStatementsForAsyncMethod(anonymousMethod.Body, semanticModel, hasReturnValue, cancellationToken));
}
if (updated == null)
if (updated is null)
{
throw new NotSupportedException();
}
@ -80,12 +77,12 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
/// </exception>
internal static async Task<Tuple<Document, MethodDeclarationSyntax>> MakeMethodAsync(this MethodDeclarationSyntax method, Document document, CancellationToken cancellationToken = default(CancellationToken))
{
if (method == null)
if (method is null)
{
throw new ArgumentNullException(nameof(method));
}
if (document == null)
if (document is null)
{
throw new ArgumentNullException(nameof(document));
}
@ -98,7 +95,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
DocumentId documentId = document.Id;
SemanticModel? semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
IMethodSymbol? methodSymbol = semanticModel.GetDeclaredSymbol(method);
IMethodSymbol? methodSymbol = semanticModel.GetDeclaredSymbol(method, cancellationToken);
bool hasReturnValue;
TypeSyntax returnType = method.ReturnType;
@ -162,7 +159,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
// Don't rename entrypoint (i.e. "Main") methods.
if (!Utils.IsEntrypointMethod(methodSymbol, semanticModel, cancellationToken))
{
var solution = await Renamer.RenameSymbolAsync(
Solution? solution = await Renamer.RenameSymbolAsync(
document.Project.Solution,
methodSymbol,
newName,
@ -171,7 +168,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
document = solution.GetDocument(document.Id);
semanticModel = null;
methodSymbol = null;
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
SyntaxNode? root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
method = (MethodDeclarationSyntax)root.GetAnnotatedNodes(methodBookmark).Single();
}
}
@ -188,8 +185,8 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
foreach (DocumentId docId in annotatedDocumentIds)
{
document = solution.GetDocument(docId);
var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
var root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false);
SyntaxTree? tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
SyntaxNode? root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false);
var rewriter = new AwaitCallRewriter(callerAnnotation);
root = rewriter.Visit(root);
solution = solution.GetDocument(tree).WithSyntaxRoot(root).Project.Solution;
@ -198,12 +195,12 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
foreach (DocumentId docId in annotatedDocumentIds)
{
document = solution.GetDocument(docId);
var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
var root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false);
for (var node = root.GetAnnotatedNodes(callerAnnotation).FirstOrDefault(); node != null; node = root.GetAnnotatedNodes(callerAnnotation).FirstOrDefault())
SyntaxTree? tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
SyntaxNode? root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false);
for (SyntaxNode? node = root.GetAnnotatedNodes(callerAnnotation).FirstOrDefault(); node is object; node = root.GetAnnotatedNodes(callerAnnotation).FirstOrDefault())
{
var callingMethod = node.FirstAncestorOrSelf<MethodDeclarationSyntax>();
if (callingMethod != null)
MethodDeclarationSyntax? callingMethod = node.FirstAncestorOrSelf<MethodDeclarationSyntax>();
if (callingMethod is object)
{
(document, callingMethod) = await MakeMethodAsync(callingMethod, document, cancellationToken).ConfigureAwait(false);
@ -229,8 +226,8 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
// Make sure we return the latest of everything.
document = solution.GetDocument(documentId);
var finalTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
var finalRoot = await finalTree.GetRootAsync(cancellationToken).ConfigureAwait(false);
SyntaxTree? finalTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
SyntaxNode? finalRoot = await finalTree.GetRootAsync(cancellationToken).ConfigureAwait(false);
method = (MethodDeclarationSyntax)finalRoot.GetAnnotatedNodes(methodBookmark).Single();
}
@ -240,20 +237,20 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
internal static async Task<Tuple<Solution, SyntaxAnnotation, List<DocumentId>>> AnnotateAllCallersAsync(Solution solution, ISymbol symbol, CancellationToken cancellationToken)
{
var bookmark = new SyntaxAnnotation();
var callers = await SymbolFinder.FindCallersAsync(symbol, solution, cancellationToken).ConfigureAwait(false);
var callersByFile = from caller in callers
IEnumerable<SymbolCallerInfo>? callers = await SymbolFinder.FindCallersAsync(symbol, solution, cancellationToken).ConfigureAwait(false);
IEnumerable<IGrouping<SyntaxTree, Location>>? callersByFile = from caller in callers
from location in caller.Locations
group location by location.SourceTree into file
select file;
var updatedDocs = new List<DocumentId>();
foreach (var callerByFile in callersByFile)
foreach (IGrouping<SyntaxTree, Location>? callerByFile in callersByFile)
{
var root = await callerByFile.Key.GetRootAsync(cancellationToken).ConfigureAwait(false);
foreach (var caller in callerByFile)
SyntaxNode? root = await callerByFile.Key.GetRootAsync(cancellationToken).ConfigureAwait(false);
foreach (Location? caller in callerByFile)
{
var node = root.FindNode(caller.SourceSpan);
var invocation = node.FirstAncestorOrSelf<InvocationExpressionSyntax>();
if (invocation != null)
SyntaxNode? node = root.FindNode(caller.SourceSpan);
InvocationExpressionSyntax? invocation = node.FirstAncestorOrSelf<InvocationExpressionSyntax>();
if (invocation is object)
{
root = root.ReplaceNode(invocation, invocation.WithAdditionalAnnotations(bookmark));
}
@ -275,7 +272,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
SyntaxNode root;
(bookmark, document, syntaxNode, root) = await BookmarkSyntaxAsync(document, syntaxNode, cancellationToken).ConfigureAwait(false);
var newSyntaxNode = syntaxNodeTransform(syntaxNode);
T? newSyntaxNode = syntaxNodeTransform(syntaxNode);
if (!newSyntaxNode.HasAnnotation(bookmark))
{
newSyntaxNode = syntaxNode.CopyAnnotationsTo(newSyntaxNode);
@ -293,7 +290,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
where T : SyntaxNode
{
var bookmark = new SyntaxAnnotation(BookmarkAnnotationName);
var root = await syntaxNode.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false);
SyntaxNode? root = await syntaxNode.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false);
root = root.ReplaceNode(syntaxNode, syntaxNode.WithAdditionalAnnotations(bookmark));
document = document.WithSyntaxRoot(root);
root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
@ -304,12 +301,12 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
internal static NameSyntax QualifyName(IReadOnlyList<string> qualifiers, SimpleNameSyntax simpleName)
{
if (qualifiers == null)
if (qualifiers is null)
{
throw new ArgumentNullException(nameof(qualifiers));
}
if (simpleName == null)
if (simpleName is null)
{
throw new ArgumentNullException(nameof(simpleName));
}
@ -322,7 +319,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
NameSyntax result = SyntaxFactory.IdentifierName(qualifiers[0]);
for (int i = 1; i < qualifiers.Count; i++)
{
var rightSide = SyntaxFactory.IdentifierName(qualifiers[i]);
IdentifierNameSyntax? rightSide = SyntaxFactory.IdentifierName(qualifiers[i]);
result = SyntaxFactory.QualifiedName(result, rightSide);
}
@ -332,14 +329,14 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
private static CSharpSyntaxNode UpdateStatementsForAsyncMethod(CSharpSyntaxNode body, SemanticModel semanticModel, bool hasResultValue, CancellationToken cancellationToken)
{
var blockBody = body as BlockSyntax;
if (blockBody != null)
if (blockBody is object)
{
bool returnTypeChanged = false; // probably not right, but we don't have a failing test yet.
return UpdateStatementsForAsyncMethod(blockBody, semanticModel, hasResultValue, returnTypeChanged, cancellationToken);
}
var expressionBody = body as ExpressionSyntax;
if (expressionBody != null)
if (expressionBody is object)
{
return SyntaxFactory.AwaitExpression(expressionBody).TrySimplify(expressionBody, semanticModel, cancellationToken);
}
@ -349,7 +346,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
private static BlockSyntax UpdateStatementsForAsyncMethod(BlockSyntax body, SemanticModel semanticModel, bool hasResultValue, bool returnTypeChanged, CancellationToken cancellationToken)
{
var fixedUpBlock = body.ReplaceNodes(
BlockSyntax? fixedUpBlock = body.ReplaceNodes(
body.DescendantNodes().OfType<ReturnStatementSyntax>(),
(f, n) =>
{
@ -376,23 +373,23 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
private static ExpressionSyntax TrySimplify(this AwaitExpressionSyntax awaitExpression, ExpressionSyntax originalSyntax, SemanticModel semanticModel, CancellationToken cancellationToken)
{
if (awaitExpression == null)
if (awaitExpression is null)
{
throw new ArgumentNullException(nameof(awaitExpression));
}
// await Task.FromResult(x) => x.
if (semanticModel != null)
if (semanticModel is object)
{
if (awaitExpression.Expression is InvocationExpressionSyntax awaitedInvocation
&& awaitedInvocation.Expression is MemberAccessExpressionSyntax awaitedInvocationMemberAccess
&& awaitedInvocationMemberAccess.Name.Identifier.Text == nameof(Task.FromResult))
{
// Is the FromResult method on the Task or Task<T> class?
var memberOwnerSymbol = semanticModel.GetSymbolInfo(originalSyntax, cancellationToken).Symbol;
ISymbol? memberOwnerSymbol = semanticModel.GetSymbolInfo(originalSyntax, cancellationToken).Symbol;
if (Utils.IsTask(memberOwnerSymbol?.ContainingType))
{
var simplified = awaitedInvocation.ArgumentList.Arguments.Single().Expression;
ExpressionSyntax? simplified = awaitedInvocation.ArgumentList.Arguments.Single().Expression;
return simplified;
}
}

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

@ -1,9 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<CodeAnalysisRuleSet>..\Microsoft.VisualStudio.Threading.Analyzers\Microsoft.VisualStudio.Threading.Analyzers.ruleset</CodeAnalysisRuleSet>
<RootNamespace>Microsoft.VisualStudio.Threading.Analyzers</RootNamespace>
<Description>Static code analyzer to detect common mistakes or potential issues regarding threading and async coding.</Description>

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

@ -1,4 +1,7 @@
namespace Microsoft.VisualStudio.Threading.Analyzers
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
using System;

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
@ -19,7 +20,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
var qualifiedNameSyntaxKind = generator.QualifiedName(generator.IdentifierName("ignored"), generator.IdentifierName("ignored")).RawKind;
var memberAccessExpressionSyntaxKind = generator.MemberAccessExpression(generator.IdentifierName("ignored"), "ignored").RawKind;
var typeExpression = generator.TypeExpression(typeSymbol);
SyntaxNode? typeExpression = generator.TypeExpression(typeSymbol);
return QualifiedNameToMemberAccess(qualifiedNameSyntaxKind, memberAccessExpressionSyntaxKind, typeExpression, generator);
// Local function
@ -27,8 +28,8 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
{
if (expression.RawKind == qualifiedNameSyntaxKind)
{
var left = QualifiedNameToMemberAccess(qualifiedNameSyntaxKind, memberAccessExpressionSyntaxKind, expression.ChildNodes().First(), generator);
var right = expression.ChildNodes().Last();
SyntaxNode? left = QualifiedNameToMemberAccess(qualifiedNameSyntaxKind, memberAccessExpressionSyntaxKind, expression.ChildNodes().First(), generator);
SyntaxNode? right = expression.ChildNodes().Last();
return generator.MemberAccessExpression(left, right);
}
@ -43,7 +44,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
return null;
}
var declarationKind = generator.GetDeclarationKind(node);
DeclarationKind declarationKind = generator.GetDeclarationKind(node);
while ((kind.HasValue && declarationKind != kind) || (!kind.HasValue && declarationKind == DeclarationKind.None))
{
node = generator.GetDeclaration(node.Parent);

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

@ -1,8 +1,5 @@
/********************************************************
* *
* © Copyright (C) Microsoft. All rights reserved. *
* *
*********************************************************/
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
@ -32,9 +29,9 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var diagnostic = context.Diagnostics.First();
Diagnostic? diagnostic = context.Diagnostics.First();
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
SyntaxNode? root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
if (TryFindNodeAtSource(diagnostic, root, out _, out _))
{
@ -43,16 +40,16 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
Strings.VSTHRD002_CodeFix_Await_Title,
async ct =>
{
var document = context.Document;
if (TryFindNodeAtSource(diagnostic, root, out var node, out var transform))
Document? document = context.Document;
if (TryFindNodeAtSource(diagnostic, root, out ExpressionSyntax? node, out Func<ExpressionSyntax, CancellationToken, ExpressionSyntax>? transform))
{
(document, node, _) = await FixUtils.UpdateDocumentAsync(
document,
node,
n => SyntaxFactory.AwaitExpression(transform(n, ct)),
ct).ConfigureAwait(false);
var method = node.FirstAncestorOrSelf<MethodDeclarationSyntax>();
if (method != null)
MethodDeclarationSyntax? method = node.FirstAncestorOrSelf<MethodDeclarationSyntax>();
if (method is object)
{
(document, method) = await FixUtils.MakeMethodAsync(method, document, ct).ConfigureAwait(false);
}
@ -74,15 +71,15 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
target = null;
var syntaxNode = (ExpressionSyntax)root.FindNode(diagnostic.Location.SourceSpan);
if (syntaxNode.FirstAncestorOrSelf<AnonymousFunctionExpressionSyntax>() != null)
if (syntaxNode.FirstAncestorOrSelf<AnonymousFunctionExpressionSyntax>() is object)
{
// We don't support converting anonymous delegates to async.
return false;
}
SimpleNameSyntax? FindStaticWaitInvocation(ExpressionSyntax from, CancellationToken cancellationToken = default(CancellationToken))
SimpleNameSyntax? FindStaticWaitInvocation(ExpressionSyntax from)
{
var name = ((from as InvocationExpressionSyntax)?.Expression as MemberAccessExpressionSyntax)?.Name;
SimpleNameSyntax? name = ((from as InvocationExpressionSyntax)?.Expression as MemberAccessExpressionSyntax)?.Name;
return name?.Identifier.ValueText switch
{
nameof(Task.WaitAny) => name,
@ -93,7 +90,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
ExpressionSyntax? TransformStaticWhatInvocation(ExpressionSyntax from, CancellationToken cancellationToken = default(CancellationToken))
{
var name = FindStaticWaitInvocation(from);
SimpleNameSyntax? name = FindStaticWaitInvocation(from);
var newIdentifier = name!.Identifier.ValueText switch
{
nameof(Task.WaitAny) => nameof(Task.WhenAny),
@ -111,27 +108,27 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
ExpressionSyntax? FindParentMemberAccess(ExpressionSyntax from, CancellationToken cancellationToken = default(CancellationToken)) =>
(from as MemberAccessExpressionSyntax)?.Expression;
var parentInvocation = syntaxNode.FirstAncestorOrSelf<InvocationExpressionSyntax>();
var parentMemberAccess = syntaxNode.FirstAncestorOrSelf<MemberAccessExpressionSyntax>();
if (FindTwoLevelDeepIdentifierInvocation(parentInvocation) != null)
InvocationExpressionSyntax? parentInvocation = syntaxNode.FirstAncestorOrSelf<InvocationExpressionSyntax>();
MemberAccessExpressionSyntax? parentMemberAccess = syntaxNode.FirstAncestorOrSelf<MemberAccessExpressionSyntax>();
if (FindTwoLevelDeepIdentifierInvocation(parentInvocation) is object)
{
// This method will not return null for the provided 'target' argument
transform = NullableHelpers.AsNonNullReturnUnchecked<ExpressionSyntax, CancellationToken, ExpressionSyntax>(FindTwoLevelDeepIdentifierInvocation);
target = parentInvocation;
}
else if (FindStaticWaitInvocation(parentInvocation) != null)
else if (FindStaticWaitInvocation(parentInvocation) is object)
{
// This method will not return null for the provided 'target' argument
transform = NullableHelpers.AsNonNullReturnUnchecked<ExpressionSyntax, CancellationToken, ExpressionSyntax>(TransformStaticWhatInvocation);
target = parentInvocation;
}
else if (FindOneLevelDeepIdentifierInvocation(parentInvocation) != null)
else if (FindOneLevelDeepIdentifierInvocation(parentInvocation) is object)
{
// This method will not return null for the provided 'target' argument
transform = NullableHelpers.AsNonNullReturnUnchecked<ExpressionSyntax, CancellationToken, ExpressionSyntax>(FindOneLevelDeepIdentifierInvocation);
target = parentInvocation;
}
else if (FindParentMemberAccess(parentMemberAccess) != null)
else if (FindParentMemberAccess(parentMemberAccess) is object)
{
// This method will not return null for the provided 'target' argument
transform = NullableHelpers.AsNonNullReturnUnchecked<ExpressionSyntax, CancellationToken, ExpressionSyntax>(FindParentMemberAccess);

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

@ -1,4 +1,7 @@
namespace Microsoft.VisualStudio.Threading.Analyzers
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
using System;
using System.Collections.Generic;
@ -30,20 +33,20 @@
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var diagnostic = context.Diagnostics.First();
Diagnostic? diagnostic = context.Diagnostics.First();
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
SyntaxNode? root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var syntaxNode = (ExpressionSyntax)root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true);
var container = CSharpUtils.GetContainingFunction(syntaxNode);
if (container.BlockOrExpression == null)
CSharpUtils.ContainingFunctionData container = CSharpUtils.GetContainingFunction(syntaxNode);
if (container.BlockOrExpression is null)
{
return;
}
var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
var enclosingSymbol = semanticModel.GetEnclosingSymbol(diagnostic.Location.SourceSpan.Start, context.CancellationToken);
if (enclosingSymbol == null)
SemanticModel? semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
ISymbol? enclosingSymbol = semanticModel.GetEnclosingSymbol(diagnostic.Location.SourceSpan.Start, context.CancellationToken);
if (enclosingSymbol is null)
{
return;
}
@ -70,10 +73,10 @@
{
// We're looking for methods that either require no parameters,
// or (if we have one to give) that have just one parameter that is a CancellationToken.
var proposedMethod = Utils.FindMethodGroup(semanticModel, option)
IMethodSymbol? proposedMethod = Utils.FindMethodGroup(semanticModel, option)
.FirstOrDefault(m => !m.Parameters.Any(p => !p.HasExplicitDefaultValue) ||
(cancellationTokenSymbol.Value != null && m.Parameters.Length == 1 && Utils.IsCancellationTokenParameter(m.Parameters[0])));
if (proposedMethod == null)
(cancellationTokenSymbol.Value is object && m.Parameters.Length == 1 && Utils.IsCancellationTokenParameter(m.Parameters[0])));
if (proposedMethod is null)
{
// We can't find it, so don't offer to use it.
continue;
@ -85,7 +88,7 @@
}
else
{
foreach (var candidate in Utils.FindInstanceOf(proposedMethod.ContainingType, semanticModel, positionForLookup, context.CancellationToken))
foreach (Tuple<bool, ISymbol>? candidate in Utils.FindInstanceOf(proposedMethod.ContainingType, semanticModel, positionForLookup, context.CancellationToken))
{
if (candidate.Item1)
{
@ -100,21 +103,21 @@
void OfferFix(string fullyQualifiedMethod)
{
context.RegisterCodeFix(CodeAction.Create($"Add call to {fullyQualifiedMethod}", ct => Fix(fullyQualifiedMethod, proposedMethod, cancellationTokenSymbol, ct), fullyQualifiedMethod), context.Diagnostics);
context.RegisterCodeFix(CodeAction.Create($"Add call to {fullyQualifiedMethod}", ct => Fix(fullyQualifiedMethod, proposedMethod, cancellationTokenSymbol), fullyQualifiedMethod), context.Diagnostics);
}
}
}
Task<Document> Fix(string fullyQualifiedMethod, IMethodSymbol methodSymbol, Lazy<ISymbol> cancellationTokenSymbol, CancellationToken cancellationToken)
Task<Document> Fix(string fullyQualifiedMethod, IMethodSymbol methodSymbol, Lazy<ISymbol> cancellationTokenSymbol)
{
int typeAndMethodDelimiterIndex = fullyQualifiedMethod.LastIndexOf('.');
IdentifierNameSyntax methodName = SyntaxFactory.IdentifierName(fullyQualifiedMethod.Substring(typeAndMethodDelimiterIndex + 1));
ExpressionSyntax invokedMethod = CSharpUtils.MemberAccess(fullyQualifiedMethod.Substring(0, typeAndMethodDelimiterIndex).Split('.'), methodName);
var invocationExpression = SyntaxFactory.InvocationExpression(invokedMethod);
var cancellationTokenParameter = methodSymbol.Parameters.FirstOrDefault(Utils.IsCancellationTokenParameter);
if (cancellationTokenParameter != null && cancellationTokenSymbol.Value != null)
InvocationExpressionSyntax? invocationExpression = SyntaxFactory.InvocationExpression(invokedMethod);
IParameterSymbol? cancellationTokenParameter = methodSymbol.Parameters.FirstOrDefault(Utils.IsCancellationTokenParameter);
if (cancellationTokenParameter is object && cancellationTokenSymbol.Value is object)
{
var arg = SyntaxFactory.Argument(SyntaxFactory.IdentifierName(cancellationTokenSymbol.Value.Name));
ArgumentSyntax? arg = SyntaxFactory.Argument(SyntaxFactory.IdentifierName(cancellationTokenSymbol.Value.Name));
if (methodSymbol.Parameters.IndexOf(cancellationTokenParameter) > 0)
{
arg = arg.WithNameColon(SyntaxFactory.NameColon(SyntaxFactory.IdentifierName(cancellationTokenParameter.Name)));
@ -124,16 +127,16 @@
}
ExpressionSyntax? awaitExpression = container.IsAsync ? SyntaxFactory.AwaitExpression(invocationExpression) : null;
var addedStatement = SyntaxFactory.ExpressionStatement(awaitExpression ?? invocationExpression)
ExpressionStatementSyntax? addedStatement = SyntaxFactory.ExpressionStatement(awaitExpression ?? invocationExpression)
.WithAdditionalAnnotations(Simplifier.Annotation, Formatter.Annotation);
var initialBlockSyntax = container.BlockOrExpression as BlockSyntax;
if (initialBlockSyntax == null)
if (initialBlockSyntax is null)
{
initialBlockSyntax = SyntaxFactory.Block(SyntaxFactory.ReturnStatement((ExpressionSyntax)container.BlockOrExpression))
.WithAdditionalAnnotations(Formatter.Annotation);
}
var newBlock = initialBlockSyntax.WithStatements(initialBlockSyntax.Statements.Insert(0, addedStatement));
BlockSyntax? newBlock = initialBlockSyntax.WithStatements(initialBlockSyntax.Statements.Insert(0, addedStatement));
return Task.FromResult(context.Document.WithSyntaxRoot(root.ReplaceNode(container.BlockOrExpression, newBlock)));
}
}

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

@ -1,8 +1,5 @@
/********************************************************
* *
* © Copyright (C) Microsoft. All rights reserved. *
* *
*********************************************************/
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
@ -52,7 +49,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
/// <inheritdoc />
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
var diagnostic = context.Diagnostics.First();
Diagnostic? diagnostic = context.Diagnostics.First();
context.RegisterCodeFix(new VoidToTaskCodeAction(context.Document, diagnostic), diagnostic);
return Task.FromResult<object?>(null);
}
@ -79,14 +76,14 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
protected override async Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
{
var root = await this.document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var methodDeclaration = root.FindNode(this.diagnostic.Location.SourceSpan).FirstAncestorOrSelf<MethodDeclarationSyntax>();
var taskType = SyntaxFactory.ParseTypeName(typeof(Task).FullName)
SyntaxNode? root = await this.document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
MethodDeclarationSyntax? methodDeclaration = root.FindNode(this.diagnostic.Location.SourceSpan).FirstAncestorOrSelf<MethodDeclarationSyntax>();
TypeSyntax? taskType = SyntaxFactory.ParseTypeName(typeof(Task).FullName)
.WithAdditionalAnnotations(Simplifier.Annotation)
.WithTrailingTrivia(methodDeclaration.ReturnType.GetTrailingTrivia());
var newMethodDeclaration = methodDeclaration.WithReturnType(taskType);
var newRoot = root.ReplaceNode(methodDeclaration, newMethodDeclaration);
var newDocument = this.document.WithSyntaxRoot(newRoot);
MethodDeclarationSyntax? newMethodDeclaration = methodDeclaration.WithReturnType(taskType);
SyntaxNode? newRoot = root.ReplaceNode(methodDeclaration, newMethodDeclaration);
Document? newDocument = this.document.WithSyntaxRoot(newRoot);
return newDocument;
}
}

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

@ -1,4 +1,7 @@
namespace Microsoft.VisualStudio.Threading.Analyzers
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
using System;
using System.Collections.Generic;
@ -40,8 +43,8 @@
/// <inheritdoc />
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var diagnostic = context.Diagnostics.FirstOrDefault(d => d.Properties.ContainsKey(VSTHRD103UseAsyncOptionAnalyzer.AsyncMethodKeyName));
if (diagnostic != null)
Diagnostic? diagnostic = context.Diagnostics.FirstOrDefault(d => d.Properties.ContainsKey(VSTHRD103UseAsyncOptionAnalyzer.AsyncMethodKeyName));
if (diagnostic is object)
{
// Check that the method we're replacing the sync blocking call with actually exists.
// This is particularly useful when the method is an extension method, since the using directive
@ -53,8 +56,8 @@
asyncMethodName = "GetAwaiter";
}
var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
SemanticModel? semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
SyntaxNode? syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var blockingIdentifier = syntaxRoot.FindNode(diagnostic.Location.SourceSpan) as IdentifierNameSyntax;
var memberAccessExpression = blockingIdentifier?.Parent as MemberAccessExpressionSyntax;
@ -64,8 +67,8 @@
if (!asyncAlternativeExists)
{
// If we fail to recognize the container, assume it exists since the analyzer thought it would.
var container = memberAccessExpression != null ? semanticModel.GetTypeInfo(memberAccessExpression.Expression, context.CancellationToken).ConvertedType : null;
asyncAlternativeExists = container == null || semanticModel.LookupSymbols(diagnostic.Location.SourceSpan.Start, name: asyncMethodName, container: container, includeReducedExtensionMethods: true).Any();
ITypeSymbol? container = memberAccessExpression is object ? semanticModel.GetTypeInfo(memberAccessExpression.Expression, context.CancellationToken).ConvertedType : null;
asyncAlternativeExists = container is null || semanticModel.LookupSymbols(diagnostic.Location.SourceSpan.Start, name: asyncMethodName, container: container, includeReducedExtensionMethods: true).Any();
}
if (asyncAlternativeExists)
@ -93,7 +96,7 @@
{
get
{
return this.AlternativeAsyncMethod != string.Empty
return !string.IsNullOrEmpty(this.AlternativeAsyncMethod)
? string.Format(CultureInfo.CurrentCulture, Strings.AwaitXInstead, this.AlternativeAsyncMethod)
: Strings.UseAwaitInstead;
}
@ -108,16 +111,16 @@
protected override async Task<Solution> GetChangedSolutionAsync(CancellationToken cancellationToken)
{
var document = this.document;
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
Document? document = this.document;
SyntaxNode? root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
// Find the synchronously blocking call member,
// and bookmark it so we can find it again after some mutations have taken place.
var syncAccessBookmark = new SyntaxAnnotation();
SimpleNameSyntax syncMethodName = (SimpleNameSyntax)root.FindNode(this.diagnostic.Location.SourceSpan);
if (syncMethodName == null)
if (syncMethodName is null)
{
var syncMemberAccess = root.FindNode(this.diagnostic.Location.SourceSpan).FirstAncestorOrSelf<MemberAccessExpressionSyntax>();
MemberAccessExpressionSyntax? syncMemberAccess = root.FindNode(this.diagnostic.Location.SourceSpan).FirstAncestorOrSelf<MemberAccessExpressionSyntax>();
syncMethodName = syncMemberAccess.Name;
}
@ -132,15 +135,15 @@
// and that renders the default semantic model broken (even though we've already updated the document's SyntaxRoot?!).
// So after acquiring the semantic model, update it with the new method body.
SemanticModel? semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var originalAnonymousMethodContainerIfApplicable = syncMethodName.FirstAncestorOrSelf<AnonymousFunctionExpressionSyntax>();
var originalMethodDeclaration = syncMethodName.FirstAncestorOrSelf<MethodDeclarationSyntax>();
AnonymousFunctionExpressionSyntax? originalAnonymousMethodContainerIfApplicable = syncMethodName.FirstAncestorOrSelf<AnonymousFunctionExpressionSyntax>();
MethodDeclarationSyntax? originalMethodDeclaration = syncMethodName.FirstAncestorOrSelf<MethodDeclarationSyntax>();
var enclosingSymbol = semanticModel.GetEnclosingSymbol(this.diagnostic.Location.SourceSpan.Start, cancellationToken);
ISymbol? enclosingSymbol = semanticModel.GetEnclosingSymbol(this.diagnostic.Location.SourceSpan.Start, cancellationToken);
var hasReturnValue = ((enclosingSymbol as IMethodSymbol)?.ReturnType as INamedTypeSymbol)?.IsGenericType ?? false;
// Ensure that the method or anonymous delegate is using the async keyword.
MethodDeclarationSyntax updatedMethod;
if (originalAnonymousMethodContainerIfApplicable != null)
if (originalAnonymousMethodContainerIfApplicable is object)
{
updatedMethod = originalMethodDeclaration.ReplaceNode(
originalAnonymousMethodContainerIfApplicable,
@ -158,14 +161,14 @@
syncMethodName = (SimpleNameSyntax)updatedMethod.GetAnnotatedNodes(syncAccessBookmark).Single();
}
var syncExpression = GetSynchronousExpression(syncMethodName);
ExpressionSyntax? syncExpression = GetSynchronousExpression(syncMethodName);
ExpressionSyntax awaitExpression;
if (this.AlternativeAsyncMethod != string.Empty)
if (!string.IsNullOrEmpty(this.AlternativeAsyncMethod))
{
// Replace the member being called and await the invocation expression.
// While doing so, move leading trivia to the surrounding await expression.
var asyncMethodName = syncMethodName.WithIdentifier(SyntaxFactory.Identifier(this.diagnostic.Properties[VSTHRD103UseAsyncOptionAnalyzer.AsyncMethodKeyName]));
SimpleNameSyntax? asyncMethodName = syncMethodName.WithIdentifier(SyntaxFactory.Identifier(this.diagnostic.Properties[VSTHRD103UseAsyncOptionAnalyzer.AsyncMethodKeyName]));
awaitExpression = SyntaxFactory.AwaitExpression(
syncExpression.ReplaceNode(syncMethodName, asyncMethodName).WithoutLeadingTrivia())
.WithLeadingTrivia(syncExpression.GetLeadingTrivia());
@ -173,8 +176,8 @@
else
{
// Remove the member being accessed that causes a synchronous block and simply await the object.
var syncMemberAccess = syncMethodName.FirstAncestorOrSelf<MemberAccessExpressionSyntax>();
var syncMemberStrippedExpression = syncMemberAccess.Expression;
MemberAccessExpressionSyntax? syncMemberAccess = syncMethodName.FirstAncestorOrSelf<MemberAccessExpressionSyntax>();
ExpressionSyntax? syncMemberStrippedExpression = syncMemberAccess.Expression;
// Special case a common pattern of calling task.GetAwaiter().GetResult() and remove both method calls.
var expressionMethodCall = (syncMemberStrippedExpression as InvocationExpressionSyntax)?.Expression as MemberAccessExpressionSyntax;
@ -196,8 +199,8 @@
updatedMethod = updatedMethod
.ReplaceNode(syncExpression, awaitExpression);
var newRoot = root.ReplaceNode(originalMethodDeclaration, updatedMethod);
var newDocument = document.WithSyntaxRoot(newRoot);
SyntaxNode? newRoot = root.ReplaceNode(originalMethodDeclaration, updatedMethod);
Document? newDocument = document.WithSyntaxRoot(newRoot);
return newDocument.Project.Solution;
}

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

@ -1,4 +1,7 @@
namespace Microsoft.VisualStudio.Threading.Analyzers
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
using System;
using System.Collections.Generic;
@ -44,16 +47,16 @@
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
var diagnostic = context.Diagnostics.First();
Diagnostic? diagnostic = context.Diagnostics.First();
context.RegisterCodeFix(
CodeAction.Create(
Strings.VSTHRD107_CodeFix_Title,
async ct =>
{
var document = context.Document;
var root = await document.GetSyntaxRootAsync(ct).ConfigureAwait(false);
var method = root.FindNode(diagnostic.Location.SourceSpan).FirstAncestorOrSelf<MethodDeclarationSyntax>();
Document? document = context.Document;
SyntaxNode? root = await document.GetSyntaxRootAsync(ct).ConfigureAwait(false);
MethodDeclarationSyntax? method = root.FindNode(diagnostic.Location.SourceSpan).FirstAncestorOrSelf<MethodDeclarationSyntax>();
(document, method, _) = await FixUtils.UpdateDocumentAsync(
document,
@ -61,10 +64,10 @@
m =>
{
root = m.SyntaxTree.GetRoot(ct);
var usingStatement = root.FindNode(diagnostic.Location.SourceSpan).FirstAncestorOrSelf<UsingStatementSyntax>();
var awaitExpression = SyntaxFactory.AwaitExpression(
UsingStatementSyntax usingStatement = root.FindNode(diagnostic.Location.SourceSpan).FirstAncestorOrSelf<UsingStatementSyntax>();
AwaitExpressionSyntax awaitExpression = SyntaxFactory.AwaitExpression(
SyntaxFactory.ParenthesizedExpression(usingStatement.Expression));
var modifiedUsingStatement = usingStatement.WithExpression(awaitExpression)
UsingStatementSyntax modifiedUsingStatement = usingStatement.WithExpression(awaitExpression)
.WithAdditionalAnnotations(Simplifier.Annotation);
return m.ReplaceNode(usingStatement, modifiedUsingStatement);
},

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

@ -1,4 +1,7 @@
namespace Microsoft.VisualStudio.Threading.Analyzers
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
using System;
using System.Collections.Generic;
@ -30,17 +33,17 @@
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
foreach (var diagnostic in context.Diagnostics)
foreach (Diagnostic? diagnostic in context.Diagnostics)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
SyntaxNode? root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
ExpressionSyntax? syntaxNode = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true) as ExpressionSyntax;
if (syntaxNode is null)
{
continue;
}
var container = CSharpUtils.GetContainingFunction(syntaxNode);
if (container.BlockOrExpression == null)
CSharpUtils.ContainingFunctionData container = CSharpUtils.GetContainingFunction(syntaxNode);
if (container.BlockOrExpression is null)
{
return;
}
@ -54,25 +57,25 @@
}
}
var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
var enclosingSymbol = semanticModel.GetEnclosingSymbol(diagnostic.Location.SourceSpan.Start, context.CancellationToken);
if (enclosingSymbol == null)
SemanticModel? semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
ISymbol? enclosingSymbol = semanticModel.GetEnclosingSymbol(diagnostic.Location.SourceSpan.Start, context.CancellationToken);
if (enclosingSymbol is null)
{
return;
}
var hasReturnValue = ((enclosingSymbol as IMethodSymbol)?.ReturnType as INamedTypeSymbol)?.IsGenericType ?? false;
var options = await CommonFixes.ReadMethodsAsync(context, CommonInterest.FileNamePatternForMethodsThatSwitchToMainThread, context.CancellationToken);
ImmutableArray<CommonInterest.QualifiedMember> options = await CommonFixes.ReadMethodsAsync(context, CommonInterest.FileNamePatternForMethodsThatSwitchToMainThread, context.CancellationToken);
int positionForLookup = diagnostic.Location.SourceSpan.Start;
ISymbol cancellationTokenSymbol = Utils.FindCancellationToken(semanticModel, positionForLookup, context.CancellationToken).FirstOrDefault();
foreach (var option in options)
foreach (CommonInterest.QualifiedMember option in options)
{
// We're looking for methods that either require no parameters,
// or (if we have one to give) that have just one parameter that is a CancellationToken.
var proposedMethod = Utils.FindMethodGroup(semanticModel, option)
IMethodSymbol? proposedMethod = Utils.FindMethodGroup(semanticModel, option)
.FirstOrDefault(m => !m.Parameters.Any(p => !p.HasExplicitDefaultValue) ||
(cancellationTokenSymbol != null && m.Parameters.Length == 1 && Utils.IsCancellationTokenParameter(m.Parameters[0])));
if (proposedMethod == null)
(cancellationTokenSymbol is object && m.Parameters.Length == 1 && Utils.IsCancellationTokenParameter(m.Parameters[0])));
if (proposedMethod is null)
{
// We can't find it, so don't offer to use it.
continue;
@ -84,7 +87,7 @@
}
else
{
foreach (var candidate in Utils.FindInstanceOf(proposedMethod.ContainingType, semanticModel, positionForLookup, context.CancellationToken))
foreach (Tuple<bool, ISymbol>? candidate in Utils.FindInstanceOf(proposedMethod.ContainingType, semanticModel, positionForLookup, context.CancellationToken))
{
if (candidate.Item1)
{
@ -105,17 +108,17 @@
async Task<Solution> Fix(string fullyQualifiedMethod, IMethodSymbol methodSymbol, bool hasReturnValue, CancellationToken cancellationToken)
{
var assertionStatementToRemove = syntaxNode!.FirstAncestorOrSelf<StatementSyntax>();
StatementSyntax? assertionStatementToRemove = syntaxNode!.FirstAncestorOrSelf<StatementSyntax>();
int typeAndMethodDelimiterIndex = fullyQualifiedMethod.LastIndexOf('.');
IdentifierNameSyntax methodName = SyntaxFactory.IdentifierName(fullyQualifiedMethod.Substring(typeAndMethodDelimiterIndex + 1));
ExpressionSyntax invokedMethod = CSharpUtils.MemberAccess(fullyQualifiedMethod.Substring(0, typeAndMethodDelimiterIndex).Split('.'), methodName)
.WithAdditionalAnnotations(Simplifier.Annotation);
var invocationExpression = SyntaxFactory.InvocationExpression(invokedMethod);
var cancellationTokenParameter = methodSymbol.Parameters.FirstOrDefault(Utils.IsCancellationTokenParameter);
if (cancellationTokenParameter != null && cancellationTokenSymbol != null)
InvocationExpressionSyntax? invocationExpression = SyntaxFactory.InvocationExpression(invokedMethod);
IParameterSymbol? cancellationTokenParameter = methodSymbol.Parameters.FirstOrDefault(Utils.IsCancellationTokenParameter);
if (cancellationTokenParameter is object && cancellationTokenSymbol is object)
{
var arg = SyntaxFactory.Argument(SyntaxFactory.IdentifierName(cancellationTokenSymbol.Name));
ArgumentSyntax? arg = SyntaxFactory.Argument(SyntaxFactory.IdentifierName(cancellationTokenSymbol.Name));
if (methodSymbol.Parameters.IndexOf(cancellationTokenParameter) > 0)
{
arg = arg.WithNameColon(SyntaxFactory.NameColon(SyntaxFactory.IdentifierName(cancellationTokenParameter.Name)));
@ -125,14 +128,14 @@
}
ExpressionSyntax awaitExpression = SyntaxFactory.AwaitExpression(invocationExpression);
var addedStatement = SyntaxFactory.ExpressionStatement(awaitExpression)
ExpressionStatementSyntax? addedStatement = SyntaxFactory.ExpressionStatement(awaitExpression)
.WithAdditionalAnnotations(Simplifier.Annotation, Formatter.Annotation);
var methodAnnotation = new SyntaxAnnotation();
CSharpSyntaxNode methodSyntax = container.Function.ReplaceNode(assertionStatementToRemove, addedStatement)
.WithAdditionalAnnotations(methodAnnotation);
Document newDocument = context.Document.WithSyntaxRoot(root.ReplaceNode(container.Function, methodSyntax));
var newSyntaxRoot = await newDocument.GetSyntaxRootAsync(cancellationToken);
SyntaxNode? newSyntaxRoot = await newDocument.GetSyntaxRootAsync(cancellationToken);
methodSyntax = (CSharpSyntaxNode)newSyntaxRoot.GetAnnotatedNodes(methodAnnotation).Single();
if (!container.IsAsync)
{

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

@ -1,4 +1,7 @@
namespace Microsoft.VisualStudio.Threading.Analyzers
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
using System;
using System.Collections.Generic;
@ -30,13 +33,13 @@
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
foreach (var diagnostic in context.Diagnostics)
foreach (Diagnostic? diagnostic in context.Diagnostics)
{
var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
SemanticModel? semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
SyntaxNode? syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var awaitedExpression = syntaxRoot.FindNode(diagnostic.Location.SourceSpan) as ExpressionSyntax;
Task<Document> ApplyFix(bool captureContext, CancellationToken cancellationToken)
Task<Document> ApplyFix(bool captureContext)
{
ExpressionSyntax configuredAwaitExpression = SyntaxFactory.ParenthesizedExpression(
SyntaxFactory.InvocationExpression(
@ -50,8 +53,8 @@
return Task.FromResult(context.Document.WithSyntaxRoot(syntaxRoot.ReplaceNode(awaitedExpression, configuredAwaitExpression)));
}
context.RegisterCodeFix(CodeAction.Create(Strings.VSTHRD111_CodeFix_True_Title, ct => ApplyFix(true, ct), true.ToString()), diagnostic);
context.RegisterCodeFix(CodeAction.Create(Strings.VSTHRD111_CodeFix_False_Title, ct => ApplyFix(false, ct), false.ToString()), diagnostic);
context.RegisterCodeFix(CodeAction.Create(Strings.VSTHRD111_CodeFix_True_Title, ct => ApplyFix(true), true.ToString()), diagnostic);
context.RegisterCodeFix(CodeAction.Create(Strings.VSTHRD111_CodeFix_False_Title, ct => ApplyFix(false), false.ToString()), diagnostic);
}
}
}

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
@ -23,10 +24,10 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
foreach (var diagnostic in context.Diagnostics)
foreach (Diagnostic? diagnostic in context.Diagnostics)
{
var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken);
var compilation = await context.Document.Project.GetCompilationAsync(context.CancellationToken);
SemanticModel? semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken);
Compilation? compilation = await context.Document.Project.GetCompilationAsync(context.CancellationToken);
if (compilation is null)
{
continue;
@ -38,9 +39,9 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
continue;
}
var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
SyntaxNode? syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
var generator = SyntaxGenerator.GetGenerator(context.Document);
var originalTypeDeclaration = generator.TryGetContainingDeclaration(syntaxRoot.FindNode(diagnostic.Location.SourceSpan));
SyntaxNode? originalTypeDeclaration = generator.TryGetContainingDeclaration(syntaxRoot.FindNode(diagnostic.Location.SourceSpan));
if (originalTypeDeclaration is null)
{
continue;
@ -52,13 +53,21 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
ct =>
{
// Declare that the type implements the System.IAsyncDisposable interface.
var newBaseType = generator.TypeExpression(bclAsyncDisposableType);
var typeDeclaration = generator.AddInterfaceType(originalTypeDeclaration, newBaseType);
SyntaxNode? newBaseType = generator.TypeExpression(bclAsyncDisposableType);
SyntaxNode? typeDeclaration = generator.AddInterfaceType(originalTypeDeclaration, newBaseType);
// Implement the interface, if we're on a non-interface type.
if (semanticModel.GetDeclaredSymbol(originalTypeDeclaration, ct) is ITypeSymbol changedSymbol && changedSymbol.TypeKind != TypeKind.Interface)
{
var disposeAsyncMethod = (IMethodSymbol)bclAsyncDisposableType.GetMembers().Single();
var statements = new SyntaxNode[]
{
generator.ReturnStatement(
generator.ObjectCreationExpression(
disposeAsyncMethod.ReturnType,
generator.InvocationExpression(
generator.MemberAccessExpression(generator.ThisExpression(), "DisposeAsync")))),
};
typeDeclaration = generator.AddMembers(
typeDeclaration,
generator.AsPrivateInterfaceImplementation(
@ -66,14 +75,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
disposeAsyncMethod.Name,
returnType: generator.TypeExpression(disposeAsyncMethod.ReturnType),
accessibility: Accessibility.Public,
statements: new SyntaxNode[]
{
generator.ReturnStatement(
generator.ObjectCreationExpression(
disposeAsyncMethod.ReturnType,
generator.InvocationExpression(
generator.MemberAccessExpression(generator.ThisExpression(), "DisposeAsync")))),
}),
statements: statements),
generator.TypeExpression(bclAsyncDisposableType)));
}

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

@ -1,4 +1,7 @@
namespace Microsoft.VisualStudio.Threading.Analyzers
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
using System.Collections.Immutable;
using System.Threading;
@ -23,27 +26,27 @@
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
foreach (var diagnostic in context.Diagnostics)
foreach (Diagnostic? diagnostic in context.Diagnostics)
{
var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var nullLiteral = syntaxRoot.FindNode(diagnostic.Location.SourceSpan);
SemanticModel? semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
SyntaxNode? syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
SyntaxNode? nullLiteral = syntaxRoot.FindNode(diagnostic.Location.SourceSpan);
if (semanticModel.GetOperation(nullLiteral, context.CancellationToken) is ILiteralOperation { ConstantValue: { HasValue: true, Value: null } })
{
var typeInfo = semanticModel.GetTypeInfo(nullLiteral, context.CancellationToken);
TypeInfo typeInfo = semanticModel.GetTypeInfo(nullLiteral, context.CancellationToken);
if (typeInfo.ConvertedType is INamedTypeSymbol returnType)
{
if (returnType.IsGenericType)
{
context.RegisterCodeFix(CodeAction.Create(Strings.VSTHRD114_CodeFix_FromResult, ct => ApplyTaskFromResultFix(returnType, ct), nameof(Task.FromResult)), diagnostic);
context.RegisterCodeFix(CodeAction.Create(Strings.VSTHRD114_CodeFix_FromResult, ct => ApplyTaskFromResultFix(returnType), nameof(Task.FromResult)), diagnostic);
}
else
{
context.RegisterCodeFix(CodeAction.Create(Strings.VSTHRD114_CodeFix_CompletedTask, ct => ApplyTaskCompletedTaskFix(returnType, ct), nameof(Task.CompletedTask)), diagnostic);
context.RegisterCodeFix(CodeAction.Create(Strings.VSTHRD114_CodeFix_CompletedTask, ct => ApplyTaskCompletedTaskFix(returnType), nameof(Task.CompletedTask)), diagnostic);
}
}
Task<Document> ApplyTaskCompletedTaskFix(INamedTypeSymbol returnType, CancellationToken cancellationToken)
Task<Document> ApplyTaskCompletedTaskFix(INamedTypeSymbol returnType)
{
var generator = SyntaxGenerator.GetGenerator(context.Document);
SyntaxNode completedTaskExpression = generator.MemberAccessExpression(
@ -53,7 +56,7 @@
return Task.FromResult(context.Document.WithSyntaxRoot(syntaxRoot.ReplaceNode(nullLiteral, completedTaskExpression)));
}
Task<Document> ApplyTaskFromResultFix(INamedTypeSymbol returnType, CancellationToken cancellationToken)
Task<Document> ApplyTaskFromResultFix(INamedTypeSymbol returnType)
{
var generator = SyntaxGenerator.GetGenerator(context.Document);
SyntaxNode taskFromResultExpression = generator.InvocationExpression(

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

@ -1,8 +1,5 @@
/********************************************************
* *
* © Copyright (C) Microsoft. All rights reserved. *
* *
*********************************************************/
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
@ -33,7 +30,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
/// <inheritdoc />
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
var diagnostic = context.Diagnostics.First();
Diagnostic? diagnostic = context.Diagnostics.First();
context.RegisterCodeFix(new AddAsyncSuffixCodeAction(context.Document, diagnostic), diagnostic);
return Task.FromResult<object?>(null);
}
@ -64,14 +61,14 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
protected override async Task<Solution> GetChangedSolutionAsync(CancellationToken cancellationToken)
{
var root = await this.document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
SyntaxNode? root = await this.document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var methodDeclaration = (MethodDeclarationSyntax)root.FindNode(this.diagnostic.Location.SourceSpan);
var semanticModel = await this.document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var methodSymbol = semanticModel.GetDeclaredSymbol(methodDeclaration, cancellationToken);
SemanticModel? semanticModel = await this.document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
IMethodSymbol? methodSymbol = semanticModel.GetDeclaredSymbol(methodDeclaration, cancellationToken);
var solution = this.document.Project.Solution;
var updatedSolution = await Renamer.RenameSymbolAsync(
Solution? solution = this.document.Project.Solution;
Solution? updatedSolution = await Renamer.RenameSymbolAsync(
solution,
methodSymbol,
this.NewName,

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

@ -1,88 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="Microsoft Managed Recommended Rules" Description="These rules focus on the most critical problems in your code, including potential security holes, application crashes, and other important logic and design errors. It is recommended to include this rule set in any custom rule set you create for your projects." ToolsVersion="10.0">
<Localization ResourceAssembly="Microsoft.VisualStudio.CodeAnalysis.RuleSets.Strings.dll" ResourceBaseName="Microsoft.VisualStudio.CodeAnalysis.RuleSets.Strings.Localized">
<Name Resource="MinimumRecommendedRules_Name" />
<Description Resource="MinimumRecommendedRules_Description" />
</Localization>
<Rules AnalyzerId="Microsoft.Analyzers.ManagedCodeAnalysis" RuleNamespace="Microsoft.Rules.Managed">
<Rule Id="CA1001" Action="Warning" />
<Rule Id="CA1009" Action="Warning" />
<Rule Id="CA1016" Action="Warning" />
<Rule Id="CA1033" Action="Warning" />
<Rule Id="CA1049" Action="Warning" />
<Rule Id="CA1060" Action="Warning" />
<Rule Id="CA1061" Action="Warning" />
<Rule Id="CA1063" Action="Warning" />
<Rule Id="CA1065" Action="Warning" />
<Rule Id="CA1301" Action="Warning" />
<Rule Id="CA1400" Action="Warning" />
<Rule Id="CA1401" Action="Warning" />
<Rule Id="CA1403" Action="Warning" />
<Rule Id="CA1404" Action="Warning" />
<Rule Id="CA1405" Action="Warning" />
<Rule Id="CA1410" Action="Warning" />
<Rule Id="CA1415" Action="Warning" />
<Rule Id="CA1821" Action="Warning" />
<Rule Id="CA1900" Action="Warning" />
<Rule Id="CA1901" Action="Warning" />
<Rule Id="CA2002" Action="Warning" />
<Rule Id="CA2100" Action="Warning" />
<Rule Id="CA2101" Action="Warning" />
<Rule Id="CA2108" Action="Warning" />
<Rule Id="CA2111" Action="Warning" />
<Rule Id="CA2112" Action="Warning" />
<Rule Id="CA2114" Action="Warning" />
<Rule Id="CA2116" Action="Warning" />
<Rule Id="CA2117" Action="Warning" />
<Rule Id="CA2122" Action="Warning" />
<Rule Id="CA2123" Action="Warning" />
<Rule Id="CA2124" Action="Warning" />
<Rule Id="CA2126" Action="Warning" />
<Rule Id="CA2131" Action="Warning" />
<Rule Id="CA2132" Action="Warning" />
<Rule Id="CA2133" Action="Warning" />
<Rule Id="CA2134" Action="Warning" />
<Rule Id="CA2137" Action="Warning" />
<Rule Id="CA2138" Action="Warning" />
<Rule Id="CA2140" Action="Warning" />
<Rule Id="CA2141" Action="Warning" />
<Rule Id="CA2146" Action="Warning" />
<Rule Id="CA2147" Action="Warning" />
<Rule Id="CA2149" Action="Warning" />
<Rule Id="CA2200" Action="Warning" />
<Rule Id="CA2202" Action="Warning" />
<Rule Id="CA2207" Action="Warning" />
<Rule Id="CA2212" Action="Warning" />
<Rule Id="CA2213" Action="Warning" />
<Rule Id="CA2214" Action="Warning" />
<Rule Id="CA2216" Action="Warning" />
<Rule Id="CA2220" Action="Warning" />
<Rule Id="CA2229" Action="Warning" />
<Rule Id="CA2231" Action="Warning" />
<Rule Id="CA2232" Action="Warning" />
<Rule Id="CA2235" Action="Warning" />
<Rule Id="CA2236" Action="Warning" />
<Rule Id="CA2237" Action="Warning" />
<Rule Id="CA2238" Action="Warning" />
<Rule Id="CA2240" Action="Warning" />
<Rule Id="CA2241" Action="Warning" />
<Rule Id="CA2242" Action="Warning" />
</Rules>
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
<Rule Id="AD0001" Action="None" />
<Rule Id="SA1636" Action="Hidden" />
<Rule Id="SA1633" Action="Hidden" />
<Rule Id="SA1202" Action="Hidden" />
<Rule Id="SA1116" Action="Hidden" />
<Rule Id="SA1117" Action="Hidden" />
<Rule Id="SA1401" Action="Hidden" />
<Rule Id="SA1124" Action="None" />
<Rule Id="SA1500" Action="Hidden" />
<Rule Id="SA1133" Action="Hidden" />
<Rule Id="SA1604" Action="Hidden" />
<Rule Id="SA1204" Action="Hidden" />
<Rule Id="SA1615" Action="Hidden" />
<Rule Id="SA1600" Action="None" />
<Rule Id="SA1601" Action="None" />
</Rules>
</RuleSet>

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

@ -1,3 +0,0 @@
using System.Runtime.InteropServices;
[assembly: ComVisible(false)]

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

@ -1,8 +1,5 @@
/********************************************************
* *
* © Copyright (C) Microsoft. All rights reserved. *
* *
*********************************************************/
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Resources;

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

@ -1,9 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<CodeAnalysisRuleSet>..\Microsoft.VisualStudio.Threading.Analyzers\Microsoft.VisualStudio.Threading.Analyzers.ruleset</CodeAnalysisRuleSet>
<RootNamespace>Microsoft.VisualStudio.Threading.Analyzers</RootNamespace>
<IsPackable>false</IsPackable>

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
@ -18,16 +19,16 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
internal override Location? GetLocationOfBaseTypeName(INamedTypeSymbol symbol, INamedTypeSymbol baseType, Compilation compilation, CancellationToken cancellationToken)
{
foreach (var syntaxReference in symbol.DeclaringSyntaxReferences)
foreach (SyntaxReference? syntaxReference in symbol.DeclaringSyntaxReferences)
{
var syntaxNode = syntaxReference.GetSyntax(cancellationToken);
SyntaxNode? syntaxNode = syntaxReference.GetSyntax(cancellationToken);
if (syntaxNode is InterfaceStatementSyntax { Parent: InterfaceBlockSyntax vbInterface })
{
if (compilation.GetSemanticModel(vbInterface.SyntaxTree) is { } semanticModel)
{
foreach (var inheritStatement in vbInterface.Inherits)
foreach (InheritsStatementSyntax? inheritStatement in vbInterface.Inherits)
{
foreach (var typeSyntax in inheritStatement.Types)
foreach (TypeSyntax? typeSyntax in inheritStatement.Types)
{
SymbolInfo baseTypeSymbolInfo = semanticModel.GetSymbolInfo(typeSyntax, cancellationToken);
if (Equals(baseTypeSymbolInfo.Symbol, baseType))
@ -42,9 +43,9 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
{
if (compilation.GetSemanticModel(vbClass.SyntaxTree) is { } semanticModel)
{
foreach (var implementStatement in vbClass.Implements)
foreach (ImplementsStatementSyntax? implementStatement in vbClass.Implements)
{
foreach (var typeSyntax in implementStatement.Types)
foreach (TypeSyntax? typeSyntax in implementStatement.Types)
{
SymbolInfo baseTypeSymbolInfo = semanticModel.GetSymbolInfo(typeSyntax, cancellationToken);
if (Equals(baseTypeSymbolInfo.Symbol, baseType))
@ -59,9 +60,9 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
{
if (compilation.GetSemanticModel(vbStruct.SyntaxTree) is { } semanticModel)
{
foreach (var implementStatement in vbStruct.Implements)
foreach (ImplementsStatementSyntax? implementStatement in vbStruct.Implements)
{
foreach (var typeSyntax in implementStatement.Types)
foreach (TypeSyntax? typeSyntax in implementStatement.Types)
{
SymbolInfo baseTypeSymbolInfo = semanticModel.GetSymbolInfo(typeSyntax, cancellationToken);
if (Equals(baseTypeSymbolInfo.Symbol, baseType))

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
@ -51,10 +52,10 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
internal void AnalyzeInvocation(OperationAnalysisContext context)
{
var invocation = (IInvocationOperation)context.Operation;
var invokeMethod = invocation.TargetMethod;
if (invokeMethod != null)
IMethodSymbol? invokeMethod = invocation.TargetMethod;
if (invokeMethod is object)
{
foreach (var legacyMethod in this.legacyThreadSwitchingMembers)
foreach (CommonInterest.QualifiedMember legacyMethod in this.legacyThreadSwitchingMembers)
{
context.CancellationToken.ThrowIfCancellationRequested();

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

@ -1,8 +1,5 @@
/********************************************************
* *
* © Copyright (C) Microsoft. All rights reserved. *
* *
*********************************************************/
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
@ -43,7 +40,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
private void AnalyzeInvocation(OperationAnalysisContext context)
{
var invocation = (IInvocationOperation)context.Operation;
var methodSymbol = invocation.TargetMethod;
IMethodSymbol? methodSymbol = invocation.TargetMethod;
if (methodSymbol.Name == Types.JoinableTaskFactory.SwitchToMainThreadAsync &&
methodSymbol.ContainingType.Name == Types.JoinableTaskFactory.TypeName &&
methodSymbol.ContainingType.BelongsToNamespace(Types.JoinableTaskFactory.Namespace))
@ -51,7 +48,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
// This is a call to JTF.SwitchToMainThreadAsync(). Is it being (directly) awaited?
if (!(invocation.Parent is IAwaitOperation))
{
var location = (this.LanguageUtils.IsolateMethodName(invocation) ?? invocation.Syntax).GetLocation();
Location? location = (this.LanguageUtils.IsolateMethodName(invocation) ?? invocation.Syntax).GetLocation();
context.ReportDiagnostic(Diagnostic.Create(Descriptor, location));
}
}

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
@ -53,11 +54,11 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
private void AnalyzeNode(OperationAnalysisContext context)
{
var objectCreation = (IObjectCreationOperation)context.Operation;
var methodSymbol = objectCreation.Constructor;
IMethodSymbol? methodSymbol = objectCreation.Constructor;
var constructedType = methodSymbol?.ReceiverType as INamedTypeSymbol;
if (Utils.IsLazyOfT(constructedType))
{
var typeArg = constructedType.TypeArguments.FirstOrDefault();
ITypeSymbol? typeArg = constructedType.TypeArguments.FirstOrDefault();
bool typeArgIsTask = typeArg?.Name == nameof(Task)
&& typeArg.BelongsToNamespace(Namespaces.SystemThreadingTasks);
if (typeArgIsTask)
@ -66,15 +67,15 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
}
else
{
var firstArgExpression = objectCreation.Arguments.FirstOrDefault()?.Value;
IOperation? firstArgExpression = objectCreation.Arguments.FirstOrDefault()?.Value;
if (firstArgExpression is IDelegateCreationOperation { Target: IAnonymousFunctionOperation anonFunc })
{
var problems = from invocation in anonFunc.Descendants().OfType<IInvocationOperation>()
System.Collections.Generic.IEnumerable<IInvocationOperation>? problems = from invocation in anonFunc.Descendants().OfType<IInvocationOperation>()
let invokedSymbol = invocation.TargetMethod
where invokedSymbol != null && CommonInterest.SyncBlockingMethods.Any(m => m.Method.IsMatch(invokedSymbol))
where invokedSymbol is object && CommonInterest.SyncBlockingMethods.Any(m => m.Method.IsMatch(invokedSymbol))
select invocation;
var firstProblem = problems.FirstOrDefault();
if (firstProblem != null)
IInvocationOperation? firstProblem = problems.FirstOrDefault();
if (firstProblem is object)
{
context.ReportDiagnostic(Diagnostic.Create(SyncBlockInValueFactoryDescriptor, this.LanguageUtils.IsolateMethodName(firstProblem).GetLocation()));
}

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
@ -46,7 +47,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
private static IArgumentOperation? GetArgumentForParameter(ImmutableArray<IArgumentOperation> arguments, IParameterSymbol parameter)
{
foreach (var argument in arguments)
foreach (IArgumentOperation? argument in arguments)
{
if (Equals(argument.Parameter, parameter))
{
@ -59,14 +60,14 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
private static void AnalyzeCall(OperationAnalysisContext context, Location location, ImmutableArray<IArgumentOperation> argList, IMethodSymbol methodSymbol, IEnumerable<IMethodSymbol> otherOverloads)
{
var firstJtfParameter = methodSymbol.Parameters.FirstOrDefault(IsImportantJtfParameter);
if (firstJtfParameter != null)
IParameterSymbol? firstJtfParameter = methodSymbol.Parameters.FirstOrDefault(IsImportantJtfParameter);
if (firstJtfParameter is object)
{
// Verify that if the JTF/JTC parameter is optional, it is actually specified in the caller's syntax.
if (firstJtfParameter.HasExplicitDefaultValue)
{
var argument = GetArgumentForParameter(argList, firstJtfParameter);
if (argument == null || argument.IsImplicit)
IArgumentOperation? argument = GetArgumentForParameter(argList, firstJtfParameter);
if (argument is null || argument.IsImplicit)
{
Diagnostic diagnostic = Diagnostic.Create(
Descriptor,
@ -95,18 +96,18 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
private void AnalyzeInvocation(OperationAnalysisContext context)
{
var invocation = (IInvocationOperation)context.Operation;
var invokedMethodName = this.LanguageUtils.IsolateMethodName(invocation);
var argList = invocation.Arguments;
var methodSymbol = invocation.TargetMethod;
SyntaxNode? invokedMethodName = this.LanguageUtils.IsolateMethodName(invocation);
ImmutableArray<IArgumentOperation> argList = invocation.Arguments;
IMethodSymbol? methodSymbol = invocation.TargetMethod;
var otherOverloads = methodSymbol.ContainingType.GetMembers(methodSymbol.Name).OfType<IMethodSymbol>();
IEnumerable<IMethodSymbol>? otherOverloads = methodSymbol.ContainingType.GetMembers(methodSymbol.Name).OfType<IMethodSymbol>();
AnalyzeCall(context, invokedMethodName.GetLocation(), argList, methodSymbol, otherOverloads);
}
private void AnalyzerObjectCreation(OperationAnalysisContext context)
{
var objectCreation = (IObjectCreationOperation)context.Operation;
var methodSymbol = objectCreation.Constructor;
IMethodSymbol? methodSymbol = objectCreation.Constructor;
AnalyzeCall(
context,
this.LanguageUtils.IsolateMethodName(objectCreation).GetLocation(),

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
@ -51,7 +52,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
private void AnalyzeInvocation(OperationAnalysisContext context)
{
var operation = (IInvocationOperation)context.Operation;
var invokeMethod = operation.TargetMethod;
IMethodSymbol? invokeMethod = operation.TargetMethod;
if (invokeMethod?.ContainingType.BelongsToNamespace(Namespaces.SystemThreadingTasks) ?? false)
{
bool reportDiagnostic = false;

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.Threading.Analyzers
{
@ -64,7 +65,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
private static bool IsInConditional(IOperation operation)
{
foreach (var ancestor in GetAncestorsWithinMethod(operation))
foreach (IOperation? ancestor in GetAncestorsWithinMethod(operation))
{
if (ancestor is IConditionalOperation ||
ancestor is IWhileLoopOperation { ConditionIsTop: true } ||
@ -92,11 +93,11 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
private static bool IsArgInInvocationToConditionalMethod(OperationAnalysisContext context)
{
var argument = GetAncestorsWithinMethod(context.Operation).OfType<IArgumentOperation>().FirstOrDefault();
IArgumentOperation? argument = GetAncestorsWithinMethod(context.Operation).OfType<IArgumentOperation>().FirstOrDefault();
var containingInvocation = argument?.Parent as IInvocationOperation;
if (containingInvocation != null)
if (containingInvocation is object)
{
var symbolOfContainingMethodInvocation = containingInvocation.TargetMethod;
IMethodSymbol? symbolOfContainingMethodInvocation = containingInvocation.TargetMethod;
return symbolOfContainingMethodInvocation?.GetAttributes().Any(a =>
a.AttributeClass.BelongsToNamespace(Namespaces.SystemDiagnostics) &&
a.AttributeClass.Name == nameof(System.Diagnostics.ConditionalAttribute)) ?? false;
@ -108,8 +109,8 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
private void AnalyzeInvocation(OperationAnalysisContext context, ImmutableArray<CommonInterest.QualifiedMember> mainThreadAssertingMethods)
{
var invocation = (IInvocationOperation)context.Operation;
var symbol = invocation.TargetMethod;
if (symbol != null)
IMethodSymbol? symbol = invocation.TargetMethod;
if (symbol is object)
{
bool reportDiagnostic = false;
if (mainThreadAssertingMethods.Contains(symbol))
@ -129,7 +130,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers
if (reportDiagnostic)
{
var nodeToLocate = this.LanguageUtils.IsolateMethodName(invocation);
SyntaxNode? nodeToLocate = this.LanguageUtils.IsolateMethodName(invocation);
context.ReportDiagnostic(Diagnostic.Create(Descriptor, nodeToLocate.GetLocation()));
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше