Apply latest Library.Template pattern
Also enable some analyzers and fix up all the warnings.
This commit is contained in:
Родитель
7437af0c86
Коммит
4de5495c75
216
.editorconfig
216
.editorconfig
|
@ -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
|
||||
|
|
|
@ -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>
|
|
@ -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
|
||||
}
|
||||
|
||||
@{
|
||||
|
|
|
@ -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'
|
|
@ -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": {
|
||||
|
|
23
init.cmd
23
init.cmd
|
@ -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%"
|
||||
)
|
||||
|
|
61
init.ps1
61
init.ps1
|
@ -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]
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче