diff --git a/.appveyor.yml b/.appveyor.yml deleted file mode 100644 index a11c10e5..00000000 --- a/.appveyor.yml +++ /dev/null @@ -1,81 +0,0 @@ -version: '{build}' -branches: - only: - - master - - /^v\d+(?:\.\d+)?$/ - - /[\b_]validate\b/ -skip_tags: true -skip_commits: - files: - - doc/* - - .github/* - - '**/*.md' - - .vsts-ci.yml -nuget: - disable_publish_on_pr: true -image: Visual Studio 2017 -configuration: Release -environment: - VisualStudioVersion: 15.0 - TreatWarningsAsErrors: true - CodeAnalysisTreatWarningsAsErrors: true -init: -- git config --global core.autocrlf true -install: - appveyor DownloadFile https://dist.nuget.org/win-x86-commandline/v4.6.1/nuget.exe -before_build: - nuget restore src -build_script: -- msbuild src\Microsoft.VisualStudio.Threading.sln /p:platform="Any CPU" /nologo /m /v:minimal /t:build,pack /bl:msbuild.binlog -- msbuild src\Microsoft.VisualStudio.Threading.sln /p:platform=x64 /nologo /m /v:minimal /bl:msbuild_x64.binlog -- msbuild src\Microsoft.VisualStudio.Threading.sln /p:platform=x86 /nologo /m /v:minimal /bl:msbuild_x86.binlog -test_script: -- >- - md "bin\%configuration%" - - SET testdir=bin\Microsoft.VisualStudio.Threading.Tests\%configuration%\ - - "%xunit20%\xunit.console.x86.exe" - "bin\Microsoft.VisualStudio.Threading.Tests\%configuration%\net451\Microsoft.VisualStudio.Threading.Tests.dll" - -noshadow - -html "bin\%configuration%\testresults_net451.html" -xml "bin\%configuration%\testresults_net451.xml" - -appveyor - -notrait "TestCategory=FailsInCloudTest" - -nologo - - "%xunit20%\xunit.console.x86.exe" - "bin\Microsoft.VisualStudio.Threading.Tests\%configuration%\net452\Microsoft.VisualStudio.Threading.Tests.dll" - "bin\Microsoft.VisualStudio.Threading.Analyzers.Tests\%configuration%\net46\Microsoft.VisualStudio.Threading.Analyzers.Tests.dll" - -noshadow - -html "bin\%configuration%\testresults_net452.html" -xml "bin\%configuration%\testresults_net452.xml" - -appveyor - -notrait "TestCategory=FailsInCloudTest" - -nologo - - "%xunit20%\xunit.console.x86.exe" - "bin\Microsoft.VisualStudio.Threading.Tests\%configuration%\net46\Microsoft.VisualStudio.Threading.Tests.dll" - -noshadow - -html "bin\%configuration%\testresults_net46.html" -xml "bin\%configuration%\testresults_net46.xml" - -appveyor - -notrait "TestCategory=FailsInCloudTest" - -nologo - - dotnet test src\Microsoft.VisualStudio.Threading.Tests\Microsoft.VisualStudio.Threading.Tests.csproj - --no-build - -f netcoreapp1.0 - --filter "TestCategory!=FailsInCloudTest" - - dotnet test src\Microsoft.VisualStudio.Threading.Tests\Microsoft.VisualStudio.Threading.Tests.csproj - --no-build - -f netcoreapp2.0 -artifacts: -- path: bin\**\*.nupkg - name: NuGet Package -- path: bin\AsyncDebugTools\x64\$(Configuration)\AsyncDebugTools.dll - name: WinDbg extension (x64) -- path: bin\AsyncDebugTools\x86\$(Configuration)\AsyncDebugTools.dll - name: WinDbg extension (x86) -- path: msbuild*.binlog - name: Build logs -- path: bin\**\testresults* - name: Test results diff --git a/.vsts-ci.yml b/.vsts-ci.yml index 11a7d74d..61fea95a 100644 --- a/.vsts-ci.yml +++ b/.vsts-ci.yml @@ -1,171 +1,43 @@ trigger: branches: - include: ["master", "v15.7", "v15.8"] + include: ["master", "v15.8"] paths: - exclude: [".github", "doc", "*.md"] + exclude: [".github", "doc", "*.md", ".appveyor.yml", ".travis.yml"] -steps: -- task: PowerShell@2 - displayName: Set VSTS variables - inputs: - targetType: inline - script: | - if ($env:SignType -eq 'Real') { - $feedGuid = '09d8d03c-1ac8-456e-9274-4d2364527d99' - } else { - $feedGuid = 'da484c78-f942-44ef-b197-99e2a1bef53c' - } +variables: + TreatWarningsAsErrors: true + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + BuildConfiguration: Release + BuildPlatform: Any CPU - Write-Host "##vso[task.setvariable variable=feedGuid]$feedGuid" +resources: + containers: + - container: xenial + image: andrewarnott/linux-buildagent - if ($env:ComputerName.StartsWith('factoryvm', [StringComparison]::OrdinalIgnoreCase)) { - Write-Host "Running on hosted queue" - Write-Host "##vso[task.setvariable variable=Hosted]true" - } +jobs: +- job: Windows + pool: Hosted VS2017 + steps: + - template: azure-pipelines/build.yml -- task: CmdLine@2 - inputs: - script: | - del /s /q "%userprofile%\.nuget\packages" - del /s /q "%LocalAppData%\NuGet\Cache" - del /s /q "%AppData%\tsd-cache" - displayName: Purge package caches - condition: and(succeeded(), ne(variables['Hosted'], 'true')) +- job: Linux + pool: + vmImage: Ubuntu 16.04 + container: xenial + variables: + GitLinkEnabled: false + steps: + - template: azure-pipelines/testfx.yml + parameters: + projectdirectory: src/Microsoft.VisualStudio.Threading.Tests -- task: NuGetToolInstaller@0 - displayName: Pin nuget.exe version - inputs: - versionSpec: 4.6.1 - -- task: MicroBuildIBCMergePlugin@0 - displayName: MicroBuild IBCMerge Plugin - inputs: - branch: master - condition: and(succeeded(), ne(variables['Hosted'], 'true')) - -- task: MicroBuildSigningPlugin@1 - displayName: MicroBuild Signing Plugin - inputs: - signType: $(SignType) - esrpSigning: true - zipSources: false - -- task: NuGetCommand@2 - inputs: - restoreSolution: '**\*.sln' - feedsToUse: config - displayName: Nuget restore packages - -- task: VSBuild@1 - inputs: - vsVersion: 15.0 - msbuildArgs: /t:build,pack /m /bl:"$(Build.ArtifactStagingDirectory)/build_logs/msbuild.binlog" - platform: Any CPU - configuration: $(BuildConfiguration) - displayName: Build Visual Studio solution - -- task: VSBuild@1 - inputs: - vsVersion: 15.0 - msbuildArgs: /t:build /m /bl:"$(Build.ArtifactStagingDirectory)/build_logs/msbuild_x86.binlog" - platform: x86 - configuration: $(BuildConfiguration) - displayName: Build AsyncDebugTools x86 - -- task: VSBuild@1 - inputs: - vsVersion: 15.0 - msbuildArgs: /t:build /m /bl:"$(Build.ArtifactStagingDirectory)/build_logs/msbuild_x64.binlog" - platform: x64 - configuration: $(BuildConfiguration) - displayName: Build AsyncDebugTools x64 - -- task: MicroBuildCleanup@1 - displayName: MicroBuild Cleanup - condition: succeededOrFailed() - -## The rest of these steps are for deployment and skipped for PR builds - -#- task: PublishBuildArtifacts@1 -# inputs: -# PathtoPublish: $(build.sourcesdirectory)/bin -# ArtifactName: bin -# ArtifactType: Container -# condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) - -- task: PublishBuildArtifacts@1 - displayName: Publish build_logs - inputs: - PathtoPublish: $(Build.ArtifactStagingDirectory)/build_logs - ArtifactName: build_logs - ArtifactType: Container - condition: succeededOrFailed() - -- task: CopyFiles@1 - displayName: Collecting symbols artifacts - inputs: - SourceFolder: bin - Contents: | - Microsoft.VisualStudio.Threading/$(BuildConfiguration)/**/Microsoft.VisualStudio.Threading.dll - Microsoft.VisualStudio.Threading/$(BuildConfiguration)/**/Microsoft.VisualStudio.Threading.pdb - Microsoft.VisualStudio.Threading.Analyzers/$(BuildConfiguration)/**/Microsoft.VisualStudio.Threading.Analyzers.dll - Microsoft.VisualStudio.Threading.Analyzers/$(BuildConfiguration)/**/Microsoft.VisualStudio.Threading.Analyzers.pdb - AsyncDebugTools/x86/$(BuildConfiguration)/AsyncDebugTools.dll - AsyncDebugTools/x86/$(BuildConfiguration)/AsyncDebugTools.pdb - AsyncDebugTools/x64/$(BuildConfiguration)/AsyncDebugTools.dll - AsyncDebugTools/x64/$(BuildConfiguration)/AsyncDebugTools.pdb - TargetFolder: $(Build.ArtifactStagingDirectory)/symbols - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) - -- task: PublishBuildArtifacts@1 - displayName: Publish symbols - inputs: - PathtoPublish: $(Build.ArtifactStagingDirectory)/symbols - ArtifactName: symbols - ArtifactType: Container - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) - -- task: CopyFiles@1 - displayName: Collecting deployable packages - inputs: - Contents: | - bin/**/$(BuildConfiguration)/**/*.nupkg - TargetFolder: $(Build.ArtifactStagingDirectory)/deployables - flattenFolders: true - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) - -- task: CopyFiles@1 - displayName: Collecting deployable binaries - inputs: - Contents: | - x86/$(BuildConfiguration)/AsyncDebugTools.dll - x64/$(BuildConfiguration)/AsyncDebugTools.dll - SourceFolder: bin/AsyncDebugTools - TargetFolder: $(Build.ArtifactStagingDirectory)/deployables - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) - -- task: PublishBuildArtifacts@1 - displayName: Publish deployables - inputs: - PathtoPublish: $(Build.ArtifactStagingDirectory)/deployables - ArtifactName: deployables - ArtifactType: Container - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) - -- task: PublishSymbols@2 - displayName: Archive symbols - inputs: - SymbolsFolder: $(Build.ArtifactStagingDirectory)/symbols - SearchPattern: '**/*.pdb' - IndexSources: false - SymbolServerType: TeamServices - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) - -- task: NuGetCommand@2 - displayName: Push NuGet packages - inputs: - command: push - searchPatternPush: '$(Build.SourcesDirectory)\bin\**\$(BuildConfiguration)\**\*.nupkg;!**\*.symbols.nupkg;!**/VS.*.nupkg' - publishVstsFeed: $(feedGuid) - allowPackageConflicts: true - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) +- job: macOS + pool: + vmImage: macOS 10.13 + variables: + GitLinkEnabled: false + steps: + - template: azure-pipelines/testfx.yml + parameters: + projectdirectory: src/Microsoft.VisualStudio.Threading.Tests diff --git a/README.md b/README.md index 3d774bb8..a77952a3 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Microsoft.VisualStudio.Threading ================================= [![NuGet package](https://img.shields.io/nuget/v/Microsoft.VisualStudio.Threading.svg)](https://nuget.org/packages/Microsoft.VisualStudio.Threading) -[![Build status](https://ci.appveyor.com/api/projects/status/kv58v4d03td5ngna/branch/master?svg=true)](https://ci.appveyor.com/project/AArnott/vs-threading/branch/master) +[![Build Status](https://andrewarnott.visualstudio.com/OSS/_apis/build/status/vs-threading)](https://andrewarnott.visualstudio.com/OSS/_build/latest?definitionId=16) [![Join the chat at https://gitter.im/vs-threading/Lobby](https://badges.gitter.im/vs-threading/Lobby.svg)](https://gitter.im/vs-threading/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) Analyzers: [![NuGet package](https://img.shields.io/nuget/v/Microsoft.VisualStudio.Threading.Analyzers.svg)](https://nuget.org/packages/Microsoft.VisualStudio.Threading.Analyzers) @@ -27,14 +27,14 @@ Analyzers: [![NuGet package](https://img.shields.io/nuget/v/Microsoft.VisualStud * Await on a `TaskScheduler` to switch to it. Switch to a background thread with `await TaskScheduler.Default;` * Await on a `Task` with a timeout - * Await on a `Task` with cancellation + * Await on a `Task` with cancellation * `JoinableTaskFactory` that allows you to schedule asynchronous or synchronous work that does not deadlock with the UI thread even when the UI thread needs to synchronously block on the result. ## Documentation -* [Overview documentation](doc/index.md) +* [Overview documentation](doc/index.md) * [Diagnostic analyzer rules](doc/analyzers/index.md) ## Supported platforms diff --git a/azure-pipelines/azure-pipeline.microbuild.after.yml b/azure-pipelines/azure-pipeline.microbuild.after.yml new file mode 100644 index 00000000..588c6845 --- /dev/null +++ b/azure-pipelines/azure-pipeline.microbuild.after.yml @@ -0,0 +1,46 @@ +steps: +- task: MicroBuildCleanup@1 + condition: and(succeededOrFailed(), ne(variables['Hosted'], 'true')) + displayName: MicroBuild Cleanup + +- task: NuGetCommand@2 + inputs: + command: push + searchPatternPush: '$(Build.SourcesDirectory)\bin\**\$(BuildConfiguration)\**\*.nupkg;!**\*.symbols.nupkg;!**/VS.*.nupkg' + publishVstsFeed: $(feedGuid) + allowPackageConflicts: true + displayName: Push packages to VSTS feed + condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) + +- task: CopyFiles@1 + inputs: + SourceFolder: bin + Contents: | + Microsoft.VisualStudio.Threading/$(BuildConfiguration)/**/Microsoft.VisualStudio.Threading.dll + Microsoft.VisualStudio.Threading/$(BuildConfiguration)/**/Microsoft.VisualStudio.Threading.pdb + Microsoft.VisualStudio.Threading.Analyzers/$(BuildConfiguration)/**/Microsoft.VisualStudio.Threading.Analyzers.dll + Microsoft.VisualStudio.Threading.Analyzers/$(BuildConfiguration)/**/Microsoft.VisualStudio.Threading.Analyzers.pdb + AsyncDebugTools/x86/$(BuildConfiguration)/AsyncDebugTools.dll + AsyncDebugTools/x86/$(BuildConfiguration)/AsyncDebugTools.pdb + AsyncDebugTools/x64/$(BuildConfiguration)/AsyncDebugTools.dll + AsyncDebugTools/x64/$(BuildConfiguration)/AsyncDebugTools.pdb + TargetFolder: $(Build.ArtifactStagingDirectory)/symbols + displayName: Collecting symbols artifacts + condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) + +- task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: $(Build.ArtifactStagingDirectory)/symbols + ArtifactName: symbols + ArtifactType: Container + displayName: Publish symbols as Azure DevOps artifacts + condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) + +- task: PublishSymbols@2 + inputs: + SymbolsFolder: $(Build.ArtifactStagingDirectory)/symbols + SearchPattern: '**/*.pdb' + IndexSources: false + SymbolServerType: TeamServices + displayName: Publish symbols to symbol server + condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) diff --git a/azure-pipelines/azure-pipeline.microbuild.before.yml b/azure-pipelines/azure-pipeline.microbuild.before.yml new file mode 100644 index 00000000..bc6b1cf6 --- /dev/null +++ b/azure-pipelines/azure-pipeline.microbuild.before.yml @@ -0,0 +1,23 @@ +steps: + +- task: CmdLine@2 + inputs: + script: | + del /s /q "%userprofile%\.nuget\packages" + del /s /q "%LocalAppData%\NuGet\Cache" + del /s /q "%AppData%\tsd-cache" + displayName: Purge package caches + condition: and(succeeded(), ne(variables['Hosted'], 'true')) + +- task: MicroBuildIBCMergePlugin@0 + inputs: + branch: rel/d15.8 + displayName: Install MicroBuild IBCMerge Plugin + condition: and(succeeded(), ne(variables['Hosted'], 'true')) + +- task: MicroBuildSigningPlugin@1 + inputs: + signType: $(SignType) + zipSources: false + displayName: Install MicroBuild Signing Plugin + condition: and(succeeded(), ne(variables['Hosted'], 'true')) diff --git a/azure-pipelines/build.yml b/azure-pipelines/build.yml new file mode 100644 index 00000000..d9791233 --- /dev/null +++ b/azure-pipelines/build.yml @@ -0,0 +1,150 @@ +steps: +- script: | + dotnet tool install --tool-path . nbgv + .\nbgv cloud -p src + displayName: Set build number + condition: ne(variables['system.pullrequest.isfork'], true) +- task: PowerShell@2 + displayName: Set VSTS variables + inputs: + targetType: inline + script: | + if ($env:SignType -eq 'Real') { + $feedGuid = '09d8d03c-1ac8-456e-9274-4d2364527d99' + } else { + $feedGuid = 'da484c78-f942-44ef-b197-99e2a1bef53c' + } + + Write-Host "##vso[task.setvariable variable=feedGuid]$feedGuid" + + if ($env:ComputerName.StartsWith('factoryvm', [StringComparison]::OrdinalIgnoreCase)) { + Write-Host "Running on hosted queue" + Write-Host "##vso[task.setvariable variable=Hosted]true" + } + + if ($env:SYSTEM_COLLECTIONID -eq '011b8bdf-6d56-4f87-be0d-0092136884d9') { + Write-Host "Running on official devdiv account: $env:System_TeamFoundationCollectionUri" + } else { + Write-Host "Running under OSS account: $env:System_TeamFoundationCollectionUri" + } + +- ${{ if eq(variables['system.collectionId'], '011b8bdf-6d56-4f87-be0d-0092136884d9') }}: + - template: azure-pipeline.microbuild.before.yml + +- task: DotNetCoreInstaller@0 + displayName: Install .NET Core SDK 2.1.300 + inputs: + packageType: sdk + version: 2.1.300 + condition: and(succeeded(), ne(variables['Hosted'], 'true')) # Hosted agents already have this. + +- script: dotnet --info + displayName: Show dotnet SDK info + +# We have to use the traditional nuget.exe for restoring since we have vcxproj projects too. +- task: NuGetToolInstaller@0 + displayName: Pin nuget.exe version + inputs: + versionSpec: 4.6.1 + +- powershell: nuget restore src\Microsoft.VisualStudio.Threading.sln + displayName: Nuget restore packages + +- task: VSBuild@1 + inputs: + vsVersion: 15.0 + msbuildArgs: /t:build,pack /m /bl:"$(Build.ArtifactStagingDirectory)/build_logs/msbuild.binlog" + platform: Any CPU + configuration: $(BuildConfiguration) + displayName: Build Visual Studio solution + +- task: VSBuild@1 + inputs: + vsVersion: 15.0 + msbuildArgs: /t:build /m /bl:"$(Build.ArtifactStagingDirectory)/build_logs/msbuild_x86.binlog" + platform: x86 + configuration: $(BuildConfiguration) + displayName: Build AsyncDebugTools x86 + +- task: VSBuild@1 + inputs: + vsVersion: 15.0 + msbuildArgs: /t:build /m /bl:"$(Build.ArtifactStagingDirectory)/build_logs/msbuild_x64.binlog" + platform: x64 + configuration: $(BuildConfiguration) + displayName: Build AsyncDebugTools x64 + +- task: DotNetCoreCLI@2 + displayName: Run tests + inputs: + command: test + projects: src/**/*.Tests.csproj + arguments: --configuration $(BuildConfiguration) --no-build --filter "TestCategory!=FailsInCloudTest" -v n + condition: and(succeeded(), ne(variables['SignType'], 'real')) + +- task: CopyFiles@1 + inputs: + Contents: | + obj/**/project.assets.json + TargetFolder: $(Build.ArtifactStagingDirectory)/projectAssetsJson + displayName: Collecting project.assets.json artifacts + condition: succeededOrFailed() + +- task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: $(Build.ArtifactStagingDirectory)/projectAssetsJson + ArtifactName: projectAssetsJson + ArtifactType: Container + displayName: Publish projectAssetsJson artifacts + condition: and(succeededOrFailed(), ne(variables['system.pullrequest.isfork'], true)) + +- task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: $(Build.ArtifactStagingDirectory)/build_logs + ArtifactName: build_logs + ArtifactType: Container + displayName: Publish build_logs artifacts + condition: and(succeededOrFailed(), ne(variables['system.pullrequest.isfork'], true)) + +## The rest of these steps are for deployment and skipped for PR builds + +#- task: PublishBuildArtifacts@1 +# inputs: +# PathtoPublish: $(build.sourcesdirectory)/bin +# ArtifactName: bin +# ArtifactType: Container +# condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'), ne(variables['system.pullrequest.isfork'], true)) + +- task: VSTest@2 + displayName: Run tests on .NET Framework (with code coverage) + inputs: + testFiltercriteria: TestCategory!=FailsInCloudTest + searchFolder: $(System.DefaultWorkingDirectory)\bin + testAssemblyVer2: | + **\*tests*.dll + platform: $(BuildPlatform) + configuration: $(BuildConfiguration) + codeCoverageEnabled: true + condition: and(succeeded(), ne(variables['SignType'], 'real')) + +- ${{ if eq(variables['system.collectionId'], '011b8bdf-6d56-4f87-be0d-0092136884d9') }}: + - template: azure-pipeline.microbuild.after.yml + +- task: CopyFiles@1 + inputs: + Contents: | + bin/**/$(BuildConfiguration)/**/*.nupkg + bin/AsyncDebugTools/x86/$(BuildConfiguration)/AsyncDebugTools.dll + bin/AsyncDebugTools/x64/$(BuildConfiguration)/AsyncDebugTools.dll + TargetFolder: $(Build.ArtifactStagingDirectory)/deployables + flattenFolders: true + displayName: Collecting deployables + condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) + +- task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: $(Build.ArtifactStagingDirectory)/deployables + ArtifactName: deployables + ArtifactType: Container + displayName: Publish deployables artifacts + condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'), ne(variables['system.pullrequest.isfork'], true)) diff --git a/azure-pipelines/official.yml b/azure-pipelines/official.yml new file mode 100644 index 00000000..15e2c00a --- /dev/null +++ b/azure-pipelines/official.yml @@ -0,0 +1,17 @@ +trigger: + branches: + include: ["master", "v15.8"] + paths: + exclude: [".github", "doc", "*.md", ".appveyor.yml", ".travis.yml"] + +variables: + TreatWarningsAsErrors: true + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + BuildConfiguration: Release + BuildPlatform: Any CPU + +jobs: +- job: Windows + pool: VSEng-MicroBuildVS2017 + steps: + - template: build.yml diff --git a/azure-pipelines/testfx.yml b/azure-pipelines/testfx.yml new file mode 100644 index 00000000..2b91fd11 --- /dev/null +++ b/azure-pipelines/testfx.yml @@ -0,0 +1,30 @@ +steps: +- script: dotnet restore + displayName: Restore packages + workingDirectory: ${{ parameters.projectdirectory }} +- script: dotnet build -v n -f netcoreapp1.0 -c $(BuildConfiguration) --no-restore /bl:"$(Build.ArtifactStagingDirectory)/build_logs/netcoreapp1.0.binlog" -p:GitLinkEnabled=false + displayName: Build test for netcoreapp1.0 + workingDirectory: ${{ parameters.projectdirectory }} +- script: dotnet build -v n -f netcoreapp2.0 -c $(BuildConfiguration) --no-restore /bl:"$(Build.ArtifactStagingDirectory)/build_logs/netcoreapp2.0.binlog" -p:GitLinkEnabled=false + displayName: Build test for netcoreapp2.0 + workingDirectory: ${{ parameters.projectdirectory }} + +- script: dotnet test -v n -f netcoreapp1.0 -c $(BuildConfiguration) --no-build --filter "TestCategory!=FailsInCloudTest" + displayName: Run tests for netcoreapp1.0 on the .NET Core 1.0.11 runtime + workingDirectory: ${{ parameters.projectdirectory }} + env: + DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX: 0 + RuntimeFrameworkVersion: 1.0.11 +- script: dotnet test -v n -f netcoreapp1.0 -c $(BuildConfiguration) --no-build --filter "TestCategory!=FailsInCloudTest" + displayName: Run tests for netcoreapp1.0 on the .NET Core 1.1.8 runtime + workingDirectory: ${{ parameters.projectdirectory }} + env: + DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX: 0 + RuntimeFrameworkVersion: 1.1.8 +- script: dotnet test -v n -f netcoreapp2.0 -c $(BuildConfiguration) --no-build --filter "TestCategory!=FailsInCloudTest" + displayName: Run tests for netcoreapp2.0 on the .NET Core 2.0.7 runtime + workingDirectory: ${{ parameters.projectdirectory }} + env: + DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX: 0 + RuntimeFrameworkVersion: 2.0.7 + enabled: false # TODO: figure out why this step hangs diff --git a/doc/analyzers/VSTHRD111.md b/doc/analyzers/VSTHRD111.md new file mode 100644 index 00000000..a211d1a0 --- /dev/null +++ b/doc/analyzers/VSTHRD111.md @@ -0,0 +1,38 @@ +# VSTHRD111 Use `.ConfigureAwait(bool)` + +Some code bases, particularly libraries with no affinity to an app's UI thread, are advised to use `.ConfigureAwait(false)` for each and every _await_ because it can avoid deadlocks after those calls start on an application's UI thread and the app later decides to synchronously block the UI thread waiting for those tasks to finish. Using `.ConfigureAwait(false)` also allows continuations to switch to a background thread even when no synchronous blocking would cause a deadlock, which makes for a more responsive application and possibly higher throughput of async operations. + +Note that this scenario can also be solved using the `JoinableTaskFactory`, but many class libraries may not wish to depend on the application proffers an instance of that type to the library. Where JoinableTaskFactory _does_ apply, use of `.ConfigureAwait(false)` is _not_ recommended. See [this topic](https://github.com/Microsoft/vs-threading/blob/master/doc/cookbook_vs.md#should-i-await-a-task-with-configureawaitfalse) for more on when `.ConfigureAwait(false)` and `.ConfigureAwait(true)` are appropriate. + +**This analyzer's diagnostics are *hidden* by default**. You should enable the rule for libraries that use to require this await suffix. + +## Examples of patterns that are flagged by this analyzer + +Any await on `Task` or `ValueTask` without the `.ConfigureAwait(bool)` method called on it will be flagged. + +```csharp +async Task FooAsync() { + await DoStuffAsync(); // This line is flagged + await DoMoreStuffAsync(); // This line is flagged +} + +async Task DoStuffAsync() { /* ... */ } +async ValueTask DoMoreStuffAsync() { /* ... */ } +``` + +## Solution + +Add `.ConfigureAwait(false)` or `.ConfigureAwait(true)` to the awaited `Task` or `ValueTask`. + +```csharp +async Task FooAsync() { + await DoStuffAsync().ConfigureAwait(true); + await DoMoreStuffAsync().ConfigureAwait(false); +} + +async Task DoStuffAsync() { /* ... */ } +async ValueTask DoMoreStuffAsync() { /* ... */ } +``` + +Code fixes are offered for for this diagnostic to add either `.ConfigureAwait(false)` or `.ConfigureAwait(true)` +to an awaited expression. diff --git a/doc/analyzers/VSTHRD200.md b/doc/analyzers/VSTHRD200.md index d475b0f3..3f87cf27 100644 --- a/doc/analyzers/VSTHRD200.md +++ b/doc/analyzers/VSTHRD200.md @@ -3,8 +3,14 @@ The .NET Guidelines for async methods includes that such methods should have names that include an "Async" suffix. +Methods that return awaitable types such as `Task` or `ValueTask` +should have an Async suffix. +Methods that do not return awaitable types should not use the Async suffix. + ## Examples of patterns that are flagged by this analyzer +This `Task`-returning method should have a name that ends with Async: + ```csharp async Task DoSomething() // analyzer flags this line { @@ -12,15 +18,30 @@ async Task DoSomething() // analyzer flags this line } ``` +This method should not have a name that ends with Async, since it does not return an awaitable type: + +```csharp +bool DoSomethingElseAsync() // analyzer flags this line +{ + return false; +} +``` + ## Solution -Simply rename the method to end in "Async": +Simply rename the method to end in "Async" (or remove the suffix, as appropriate): ```csharp async Task DoSomethingAsync() { await Task.Yield(); } + +bool DoSomethingElse() +{ + return false; +} ``` + A code fix exists to automatically rename such methods. diff --git a/doc/analyzers/index.md b/doc/analyzers/index.md index 591dc8ff..d31011f8 100644 --- a/doc/analyzers/index.md +++ b/doc/analyzers/index.md @@ -23,6 +23,7 @@ ID | Title | Severity | Supports [VSTHRD108](VSTHRD108.md) | Assert thread affinity unconditionally | Advisory | [1st rule](../threading_rules.md#Rule1), [VSTHRD010](VSTHRD010.md) [VSTHRD109](VSTHRD109.md) | Switch instead of assert in async methods | Advisory | [1st rule](../threading_rules.md#Rule1) [VSTHRD110](VSTHRD110.md) | Observe result of async calls | Advisory +[VSTHRD110](VSTHRD111.md) | Use `.ConfigureAwait(bool)` | Advisory [VSTHRD200](VSTHRD200.md) | Use `Async` naming convention | Guideline | [VSTHRD103](VSTHRD103.md) ## Severity descriptions diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/CSharpCodeFixVerifier`2+Test.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/CSharpCodeFixVerifier`2+Test.cs index 851fb8a9..b6d8a9cb 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/CSharpCodeFixVerifier`2+Test.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Helpers/CSharpCodeFixVerifier`2+Test.cs @@ -7,6 +7,8 @@ namespace Microsoft.VisualStudio.Threading.Analyzers.Tests using System.IO; using System.Linq; using System.Reflection; + using System.Runtime.CompilerServices; + using System.Threading.Tasks; using System.Windows.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -20,11 +22,19 @@ namespace Microsoft.VisualStudio.Threading.Analyzers.Tests public class Test : CSharpCodeFixTest { private static readonly ImmutableArray VSSDKPackageReferences = ImmutableArray.Create(new string[] { - Path.Combine("Microsoft.VisualStudio.Shell.Interop", "7.10.6071", "lib", "Microsoft.VisualStudio.Shell.Interop.dll"), - Path.Combine("Microsoft.VisualStudio.Shell.Interop.11.0", "11.0.61030", "lib", "Microsoft.VisualStudio.Shell.Interop.11.0.dll"), - Path.Combine("Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime", "14.3.25407", "lib", "Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime.dll"), - Path.Combine("Microsoft.VisualStudio.Shell.Immutable.14.0", "14.3.25407", "lib\\net45", "Microsoft.VisualStudio.Shell.Immutable.14.0.dll"), - Path.Combine("Microsoft.VisualStudio.Shell.14.0", "14.3.25407", "lib", "Microsoft.VisualStudio.Shell.14.0.dll"), + "Microsoft.VisualStudio.Shell.Interop.dll", + "Microsoft.VisualStudio.Shell.Interop.11.0.dll", + "Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime.dll", + "Microsoft.VisualStudio.Shell.Immutable.14.0.dll", + "Microsoft.VisualStudio.Shell.14.0.dll", + }); + + private static readonly ImmutableArray AdditionalReferencesFromBinFolder = ImmutableArray.Create(new string[] { + "System.Threading.Tasks.Extensions.dll", + }); + + private static readonly ImmutableArray AdditionalReferencesFromGAC = ImmutableArray.Create(new string[] { + "System.Threading.Tasks, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", }); public Test() @@ -32,7 +42,9 @@ namespace Microsoft.VisualStudio.Threading.Analyzers.Tests this.SolutionTransforms.Add((solution, projectId) => { var parseOptions = (CSharpParseOptions)solution.GetProject(projectId).ParseOptions; - solution = solution.WithProjectParseOptions(projectId, parseOptions.WithLanguageVersion(LanguageVersion.CSharp7_1)); + solution = solution.WithProjectParseOptions(projectId, parseOptions.WithLanguageVersion(LanguageVersion.CSharp7_1)) + .AddMetadataReferences(projectId, AdditionalReferencesFromBinFolder.Select(f => MetadataReference.CreateFromFile(Path.Combine(Environment.CurrentDirectory, f)))) + .AddMetadataReferences(projectId, AdditionalReferencesFromGAC.Select(assemblyName => MetadataReference.CreateFromFile(Assembly.Load(assemblyName).Location))); if (this.HasEntryPoint) { @@ -54,7 +66,7 @@ namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { solution = solution.AddMetadataReference(projectId, MetadataReference.CreateFromFile(typeof(IOleServiceProvider).Assembly.Location)); - var nugetPackagesFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages"); + var nugetPackagesFolder = Environment.CurrentDirectory; foreach (var reference in VSSDKPackageReferences) { solution = solution.AddMetadataReference(projectId, MetadataReference.CreateFromFile(Path.Combine(nugetPackagesFolder, reference))); @@ -91,4 +103,4 @@ namespace Microsoft.VisualStudio.Threading.Analyzers.Tests } } } -} \ No newline at end of file +} diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Microsoft.VisualStudio.Threading.Analyzers.Tests.csproj b/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Microsoft.VisualStudio.Threading.Analyzers.Tests.csproj index 90d0e1c2..c4303ea9 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Microsoft.VisualStudio.Threading.Analyzers.Tests.csproj +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/Microsoft.VisualStudio.Threading.Analyzers.Tests.csproj @@ -21,10 +21,11 @@ + - - - + + + @@ -50,4 +51,4 @@ AdditionalFiles - \ No newline at end of file + diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/MultiAnalyzerTests.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/MultiAnalyzerTests.cs index 9b70c5e7..36f2155a 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/MultiAnalyzerTests.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/MultiAnalyzerTests.cs @@ -28,7 +28,7 @@ class Test { Task FooAsync() { Task t = Task.FromResult(1); t.GetAwaiter().GetResult(); // VSTHRD002, VSTHRD103, VSTHRD102 - jtf.Run(async delegate { await BarAsync(); }); // VSTHRD103, VSTHRD102 + jtf.Run(async delegate { await BarAsync().ConfigureAwait(true); }); // VSTHRD103, VSTHRD102 return Task.FromResult(1); } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD010MainThreadUsageAnalyzerTests.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD010MainThreadUsageAnalyzerTests.cs index f0e4d881..b4f1be3b 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD010MainThreadUsageAnalyzerTests.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD010MainThreadUsageAnalyzerTests.cs @@ -61,10 +61,6 @@ class Test { TestCode = test, ExpectedDiagnostics = { expected }, FixedCode = fix, - - // Expected: SimpleMemberAccessExpression - // Actual: QualifiedName - CodeFixValidationMode = CodeFixValidationMode.None, }.RunAsync(); } @@ -100,10 +96,6 @@ class Test { TestCode = test, ExpectedDiagnostics = { expected }, FixedCode = fix, - - // Expected: SimpleMemberAccessExpression - // Actual: QualifiedName - CodeFixValidationMode = CodeFixValidationMode.None, }.RunAsync(); } @@ -189,10 +181,6 @@ class Test { TestCode = test, ExpectedDiagnostics = { expected }, FixedCode = fix, - - // Expected: SimpleMemberAccessExpression - // Actual: QualifiedName - CodeFixValidationMode = CodeFixValidationMode.None, }.RunAsync(); } @@ -226,10 +214,6 @@ class Test { TestCode = test, ExpectedDiagnostics = { expected }, FixedCode = fix, - - // Expected: SimpleMemberAccessExpression - // Actual: QualifiedName - CodeFixValidationMode = CodeFixValidationMode.None, }.RunAsync(); } @@ -261,7 +245,7 @@ using Microsoft.VisualStudio.Shell.Interop; class Test { Test() { - Test.VerifyOnUIThread(); + VerifyOnUIThread(); Foo(); } @@ -303,10 +287,6 @@ class Test { ExpectedDiagnostics = { expected }, FixedCode = fix1, CodeFixIndex = CodeFixIndex.VerifyOnUIThread, - - // Expected: SimpleMemberAccessExpression - // Actual: QualifiedName - CodeFixValidationMode = CodeFixValidationMode.None, }.RunAsync(); await new Verify.Test @@ -315,10 +295,6 @@ class Test { ExpectedDiagnostics = { expected }, FixedCode = fix2, CodeFixIndex = CodeFixIndex.ThrowIfNotOnUIThreadIndex1, - - // Expected: SimpleMemberAccessExpression - // Actual: QualifiedName - CodeFixValidationMode = CodeFixValidationMode.None, }.RunAsync(); } @@ -384,10 +360,6 @@ class Test { ExpectedDiagnostics = { Verify.Diagnostic(DescriptorSync).WithSpan(8, 13, 8, 24).WithArguments("IVsSolution", "Test.VerifyOnUIThread") }, FixedCode = fix, CodeFixIndex = CodeFixIndex.ThrowIfNotOnUIThreadIndex0, - - // Expected: SimpleMemberAccessExpression - // Actual: QualifiedName - CodeFixValidationMode = CodeFixValidationMode.None, }.RunAsync(); } @@ -542,7 +514,7 @@ class Test { } void H() { - Test.VerifyOnUIThread(); + VerifyOnUIThread(); F(); G(); } @@ -561,10 +533,6 @@ class Test { }, FixedCode = fix, CodeFixIndex = CodeFixIndex.VerifyOnUIThread, - - // Expected: SimpleMemberAccessExpression - // Actual: QualifiedName - CodeFixValidationMode = CodeFixValidationMode.None, }.RunAsync(); } @@ -769,10 +737,6 @@ class Test { ExpectedDiagnostics = { Verify.Diagnostic(DescriptorSync).WithSpan(11, 17, 11, 28).WithArguments("IVsSolution", "Test.VerifyOnUIThread") }, FixedCode = fix, CodeFixIndex = CodeFixIndex.VerifyOnUIThread, - - // Expected: SimpleMemberAccessExpression - // Actual: QualifiedName - CodeFixValidationMode = CodeFixValidationMode.None, }.RunAsync(); } @@ -824,10 +788,6 @@ class Test { ExpectedDiagnostics = { Verify.Diagnostic(DescriptorSync).WithSpan(12, 17, 12, 28).WithArguments("IVsSolution", "Test.VerifyOnUIThread") }, FixedCode = fix, CodeFixIndex = CodeFixIndex.VerifyOnUIThread, - - // Expected: SimpleMemberAccessExpression - // Actual: QualifiedName - CodeFixValidationMode = CodeFixValidationMode.None, }.RunAsync(); } @@ -901,10 +861,6 @@ class Test { ExpectedDiagnostics = { Verify.Diagnostic(DescriptorSync).WithSpan(11, 17, 11, 28).WithArguments("IVsSolution", "Test.VerifyOnUIThread") }, FixedCode = fix, CodeFixIndex = CodeFixIndex.VerifyOnUIThread, - - // Expected: SimpleMemberAccessExpression - // Actual: QualifiedName - CodeFixValidationMode = CodeFixValidationMode.None, }.RunAsync(); } @@ -1298,10 +1254,6 @@ class Test : AsyncPackage { }, FixedCode = fix, CodeFixIndex = CodeFixIndex.NotThreadHelper, - - // Expected: SimpleMemberAccessExpression - // Actual: QualifiedName - CodeFixValidationMode = CodeFixValidationMode.None, }.RunAsync(); } @@ -1345,10 +1297,6 @@ class Test : AsyncPackage { TestCode = test, ExpectedDiagnostics = { expected }, FixedCode = fix, - - // Expected: SimpleMemberAccessExpression - // Actual: QualifiedName - CodeFixValidationMode = CodeFixValidationMode.None, }.RunAsync(); } @@ -1439,10 +1387,6 @@ class Test : AsyncPackage { ExpectedDiagnostics = { Verify.Diagnostic(DescriptorAsync).WithSpan(13, 65, 13, 76).WithArguments("IVsShell", "JoinableTaskFactory.SwitchToMainThreadAsync") }, FixedCode = fix, CodeFixIndex = CodeFixIndex.NotThreadHelper, - - // Expected: SimpleMemberAccessExpression - // Actual: QualifiedName - CodeFixValidationMode = CodeFixValidationMode.None, }.RunAsync(); } @@ -1484,7 +1428,7 @@ class Test : AsyncPackage { static Task MySwitchingMethodAsync(bool foo = false, CancellationToken ct = default(CancellationToken)) => TplExtensions.CompletedTask; protected override async Task InitializeAsync(System.Threading.CancellationToken cancellationToken, IProgress progress) { - await Test.MySwitchingMethodAsync(ct: cancellationToken); + await MySwitchingMethodAsync(ct: cancellationToken); await base.InitializeAsync(cancellationToken, progress); var shell = await asp.GetServiceAsync(typeof(SVsShell)) as IVsShell; } @@ -1496,10 +1440,6 @@ class Test : AsyncPackage { ExpectedDiagnostics = { Verify.Diagnostic(DescriptorAsync).WithSpan(17, 65, 17, 76).WithArguments("IVsShell", "JoinableTaskFactory.SwitchToMainThreadAsync") }, FixedCode = fix, CodeFixIndex = CodeFixIndex.MySwitchingMethodAsync, - - // Expected: SimpleMemberAccessExpression - // Actual: QualifiedName - CodeFixValidationMode = CodeFixValidationMode.None, }.RunAsync(); } @@ -1681,10 +1621,6 @@ class A TestCode = test, ExpectedDiagnostics = { expected }, FixedCode = fix, - - // Expected: SimpleMemberAccessExpression - // Actual: QualifiedName - CodeFixValidationMode = CodeFixValidationMode.None, }.RunAsync(); } @@ -1724,10 +1660,6 @@ class A TestCode = test, ExpectedDiagnostics = { expected }, FixedCode = fix, - - // Expected: SimpleMemberAccessExpression - // Actual: QualifiedName - CodeFixValidationMode = CodeFixValidationMode.None, }.RunAsync(); } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD111UseConfigureAwaitAnalyzerTests.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD111UseConfigureAwaitAnalyzerTests.cs new file mode 100644 index 00000000..96565d2e --- /dev/null +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD111UseConfigureAwaitAnalyzerTests.cs @@ -0,0 +1,172 @@ +namespace Microsoft.VisualStudio.Threading.Analyzers.Tests +{ + using System.Threading.Tasks; + using Microsoft.CodeAnalysis.Testing; + using Xunit; + using Verify = CSharpCodeFixVerifier; + + public class VSTHRD111UseConfigureAwaitAnalyzerTests + { + [Fact] + public async Task AwaitOnTask_NoSuffix_GeneratesDiagnostic() + { + var test = @" +using System.Threading.Tasks; + +class Test { + async Task Foo() + { + await [|BarAsync()|]; + } + + Task BarAsync() => default; +} +"; + var fixFalse = @" +using System.Threading.Tasks; + +class Test { + async Task Foo() + { + await BarAsync().ConfigureAwait(false); + } + + Task BarAsync() => default; +} +"; + var fixTrue = @" +using System.Threading.Tasks; + +class Test { + async Task Foo() + { + await BarAsync().ConfigureAwait(true); + } + + Task BarAsync() => default; +} +"; + + await new Verify.Test + { + TestCode = test, + FixedCode = fixFalse, + CodeFixEquivalenceKey = false.ToString(), + }.RunAsync(); + await new Verify.Test + { + TestCode = test, + FixedCode = fixTrue, + CodeFixEquivalenceKey = true.ToString(), + }.RunAsync(); + } + + [Fact] + public async Task AwaitOnValueTask_NoSuffix_GeneratesDiagnostic() + { + var test = @" +using System.Threading.Tasks; + +class Test { + async Task Foo() + { + await [|BarAsync()|]; + } + + ValueTask BarAsync() => default; +} +"; + var fixFalse = @" +using System.Threading.Tasks; + +class Test { + async Task Foo() + { + await BarAsync().ConfigureAwait(false); + } + + ValueTask BarAsync() => default; +} +"; + var fixTrue = @" +using System.Threading.Tasks; + +class Test { + async Task Foo() + { + await BarAsync().ConfigureAwait(true); + } + + ValueTask BarAsync() => default; +} +"; + + await new Verify.Test + { + TestCode = test, + FixedCode = fixFalse, + CodeFixEquivalenceKey = false.ToString(), + }.RunAsync(); + await new Verify.Test + { + TestCode = test, + FixedCode = fixTrue, + CodeFixEquivalenceKey = true.ToString(), + }.RunAsync(); + } + + [Fact] + public async Task AwaitOnTaskOfT_NoSuffix_GeneratesDiagnostic() + { + var test = @" +using System.Threading.Tasks; + +class Test { + async Task Foo() + { + int total = await [|BarAsync()|] + 3; + } + + Task BarAsync() => default; +} +"; + var fixFalse = @" +using System.Threading.Tasks; + +class Test { + async Task Foo() + { + int total = await BarAsync().ConfigureAwait(false) + 3; + } + + Task BarAsync() => default; +} +"; + var fixTrue = @" +using System.Threading.Tasks; + +class Test { + async Task Foo() + { + int total = await BarAsync().ConfigureAwait(true) + 3; + } + + Task BarAsync() => default; +} +"; + + await new Verify.Test + { + TestCode = test, + FixedCode = fixFalse, + CodeFixEquivalenceKey = false.ToString(), + }.RunAsync(); + await new Verify.Test + { + TestCode = test, + FixedCode = fixTrue, + CodeFixEquivalenceKey = true.ToString(), + }.RunAsync(); + } + } +} diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD200UseAsyncNamingConventionAnalyzerTests.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD200UseAsyncNamingConventionAnalyzerTests.cs index 0f646c54..8c50d19d 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD200UseAsyncNamingConventionAnalyzerTests.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD200UseAsyncNamingConventionAnalyzerTests.cs @@ -1,12 +1,17 @@ namespace Microsoft.VisualStudio.Threading.Analyzers.Tests { using System.Threading.Tasks; + using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Testing; using Xunit; using Verify = CSharpCodeFixVerifier; public class VSTHRD200UseAsyncNamingConventionAnalyzerTests { + private static readonly DiagnosticDescriptor AddSuffixDescriptor = VSTHRD200UseAsyncNamingConventionAnalyzer.AddAsyncDescriptor; + + private static readonly DiagnosticDescriptor RemoveSuffixDescriptor = VSTHRD200UseAsyncNamingConventionAnalyzer.RemoveAsyncDescriptor; + [Fact] public async Task TaskReturningMethodWithoutSuffix_GeneratesWarning() { @@ -26,7 +31,53 @@ class Test { } "; - var expected = Verify.Diagnostic().WithSpan(5, 10, 5, 13); + var expected = Verify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 10, 5, 13); + await Verify.VerifyCodeFixAsync(test, expected, withFix); + } + + [Fact] + public async Task ValueTaskReturningMethodWithoutSuffix_GeneratesWarning() + { + var test = @" +using System.Threading.Tasks; + +class Test { + ValueTask Foo() => default; +} +"; + + var withFix = @" +using System.Threading.Tasks; + +class Test { + ValueTask FooAsync() => default; +} +"; + + var expected = Verify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 15, 5, 18); + await Verify.VerifyCodeFixAsync(test, expected, withFix); + } + + [Fact] + public async Task ValueTaskOfTReturningMethodWithoutSuffix_GeneratesWarning() + { + var test = @" +using System.Threading.Tasks; + +class Test { + ValueTask Foo() => default; +} +"; + + var withFix = @" +using System.Threading.Tasks; + +class Test { + ValueTask FooAsync() => default; +} +"; + + var expected = Verify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 20, 5, 23); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -121,7 +172,7 @@ class Test { } "; - var expected = Verify.Diagnostic().WithSpan(5, 10, 5, 13); + var expected = Verify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 10, 5, 13); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -158,8 +209,8 @@ class Test { DiagnosticResult[] expected = { - Verify.Diagnostic().WithSpan(5, 10, 5, 13), - Verify.Diagnostic().WithSpan(6, 10, 6, 13), + Verify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 10, 5, 13), + Verify.Diagnostic(AddSuffixDescriptor).WithSpan(6, 10, 6, 13), }; await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -177,6 +228,52 @@ class Test { await Verify.VerifyAnalyzerAsync(test); } + [Fact] + public async Task VoidReturningMethodWithSuffix_GeneratesWarning() + { + var test = @" +class Test { + void FooAsync() { } + + void Bar() => FooAsync(); +} +"; + + var withFix = @" +class Test { + void Foo() { } + + void Bar() => Foo(); +} +"; + + var expected = Verify.Diagnostic(RemoveSuffixDescriptor).WithSpan(3, 10, 3, 18); + await Verify.VerifyCodeFixAsync(test, expected, withFix); + } + + [Fact] + public async Task BoolReturningMethodWithSuffix_GeneratesWarning() + { + var test = @" +class Test { + bool FooAsync() => false; + + bool Bar() => FooAsync(); +} +"; + + var withFix = @" +class Test { + bool Foo() => false; + + bool Bar() => Foo(); +} +"; + + var expected = Verify.Diagnostic(RemoveSuffixDescriptor).WithSpan(3, 10, 3, 18); + await Verify.VerifyCodeFixAsync(test, expected, withFix); + } + [Fact] public async Task TaskReturningMethodWithoutSuffix_ImplementsInterface_GeneratesWarningOnlyOnInterface() { @@ -204,7 +301,7 @@ class Test : IFoo { } "; - var expected = Verify.Diagnostic().WithSpan(5, 10, 5, 13); + var expected = Verify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 10, 5, 13); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -235,7 +332,7 @@ class Test : IFoo { } "; - var expected = Verify.Diagnostic().WithSpan(5, 10, 5, 13); + var expected = Verify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 10, 5, 13); await Verify.VerifyCodeFixAsync(test, expected, withFix); } @@ -266,7 +363,7 @@ class Test : MyBase { } "; - var expected = Verify.Diagnostic().WithSpan(5, 26, 5, 29); + var expected = Verify.Diagnostic(AddSuffixDescriptor).WithSpan(5, 26, 5, 29); await Verify.VerifyCodeFixAsync(test, expected, withFix); } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Microsoft.VisualStudio.Threading.Analyzers.csproj b/src/Microsoft.VisualStudio.Threading.Analyzers/Microsoft.VisualStudio.Threading.Analyzers.csproj index 9f50c548..625e544c 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/Microsoft.VisualStudio.Threading.Analyzers.csproj +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/Microsoft.VisualStudio.Threading.Analyzers.csproj @@ -62,7 +62,6 @@ - diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.cs.xlf b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.cs.xlf index 108df5a3..0d24c122 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.cs.xlf +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.cs.xlf @@ -1,4 +1,4 @@ - +
@@ -104,10 +104,6 @@ Použijte raději AsyncLazy<T>. Přejmenování na {0} {0} is a method name. - - Use "Async" suffix in names of Task-returning methods. - Příponu Async použijte v názvech metod, které vrací Task. - Use "Async" suffix for async methods Použití přípony Async pro asynchronní metody @@ -219,6 +215,34 @@ Use AsyncLazy<T> instead. Vyvolání nebo zablokování asynchronního kódu v objektu pro vytváření hodnot Lazy<it id="1" pos="open"><T></it> může způsobit zablokování. Použijte raději AsyncLazy<it id="2" pos="open"><T></it>. + + Use "Async" suffix in names of methods that return an awaitable type. + Use "Async" suffix in names of methods that return an awaitable type. + + + Avoid "Async" suffix in names of methods that do not return an awaitable type. + Avoid "Async" suffix in names of methods that do not return an awaitable type. + + + Add .ConfigureAwait(false) + Add .ConfigureAwait(false) + ".ConfigureAwait(false)" should not be translated. + + + Add .ConfigureAwait(true) + Add .ConfigureAwait(true) + ".ConfigureAwait(true)" should not be translated. + + + Add .ConfigureAwait(bool) to your await expression. + Add .ConfigureAwait(bool) to your await expression. + ".ConfigureAwait(bool)" and "await" should NOT be translated. + + + Use ConfigureAwait(bool) + Use ConfigureAwait(bool) + "ConfigureAwait(bool)" is a reference and should NOT be translated. + diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.de.xlf b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.de.xlf index 83d19e1c..205ee15e 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.de.xlf +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.de.xlf @@ -1,4 +1,4 @@ - +
@@ -104,10 +104,6 @@ Verwenden Sie stattdessen "AsyncLazy<T>". In {0} umbenennen {0} is a method name. - - Use "Async" suffix in names of Task-returning methods. - Verwenden Sie das Suffix "Async" in Namen von Methoden, die Tasks zurückgeben. - Use "Async" suffix for async methods Verwenden Sie das Suffix "Async" für asynchrone Methoden @@ -219,6 +215,34 @@ Use AsyncLazy<T> instead. Das Aufrufen oder Blockieren von asynchronem Code in einer Lazy<it id="1" pos="open"><T></it>-Wertfactory kann zu einem Deadlock führen. Verwenden Sie stattdessen "AsyncLazy<it id="2" pos="open"><T></it>". + + Use "Async" suffix in names of methods that return an awaitable type. + Use "Async" suffix in names of methods that return an awaitable type. + + + Avoid "Async" suffix in names of methods that do not return an awaitable type. + Avoid "Async" suffix in names of methods that do not return an awaitable type. + + + Add .ConfigureAwait(false) + Add .ConfigureAwait(false) + ".ConfigureAwait(false)" should not be translated. + + + Add .ConfigureAwait(true) + Add .ConfigureAwait(true) + ".ConfigureAwait(true)" should not be translated. + + + Add .ConfigureAwait(bool) to your await expression. + Add .ConfigureAwait(bool) to your await expression. + ".ConfigureAwait(bool)" and "await" should NOT be translated. + + + Use ConfigureAwait(bool) + Use ConfigureAwait(bool) + "ConfigureAwait(bool)" is a reference and should NOT be translated. + diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.es.xlf b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.es.xlf index 5e957472..b086ec73 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.es.xlf +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.es.xlf @@ -1,4 +1,4 @@ - +
@@ -104,10 +104,6 @@ Use AsyncLazy<T> en su lugar. Cambie el nombre a {0} {0} is a method name. - - Use "Async" suffix in names of Task-returning methods. - Use el sufijo "Async" en nombres de métodos de devolución de elementos Task - Use "Async" suffix for async methods Use el sufijo "Async" para métodos asincrónicos @@ -219,6 +215,34 @@ Use AsyncLazy<T> instead. Invocar o bloquear en código asincrónico en un generador de valores Lazy<it id="1" pos="open"><T></it> puede provocar interbloqueos. Use en su lugar AsyncLazy<it id="2" pos="open"><T></it>. + + Use "Async" suffix in names of methods that return an awaitable type. + Use "Async" suffix in names of methods that return an awaitable type. + + + Avoid "Async" suffix in names of methods that do not return an awaitable type. + Avoid "Async" suffix in names of methods that do not return an awaitable type. + + + Add .ConfigureAwait(false) + Add .ConfigureAwait(false) + ".ConfigureAwait(false)" should not be translated. + + + Add .ConfigureAwait(true) + Add .ConfigureAwait(true) + ".ConfigureAwait(true)" should not be translated. + + + Add .ConfigureAwait(bool) to your await expression. + Add .ConfigureAwait(bool) to your await expression. + ".ConfigureAwait(bool)" and "await" should NOT be translated. + + + Use ConfigureAwait(bool) + Use ConfigureAwait(bool) + "ConfigureAwait(bool)" is a reference and should NOT be translated. + diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.fr.xlf b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.fr.xlf index 6a0cb55f..394d1693 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.fr.xlf +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.fr.xlf @@ -1,4 +1,4 @@ - +
@@ -104,10 +104,6 @@ Utilisez AsyncLazy<T> à la place Renommer en {0} {0} is a method name. - - Use "Async" suffix in names of Task-returning methods. - Utilisez le suffixe "Async" dans les noms des méthodes retournant Task. - Use "Async" suffix for async methods Utiliser le suffixe "Async" pour les méthodes asynchrones @@ -219,6 +215,34 @@ Use AsyncLazy<T> instead. Les appels ou blocages sur le code async dans une fabrique de valeurs Lazy<it id="1" pos="open"><T></it> peuvent faire l'objet d'un interblocage. Utilisez plutôt AsyncLazy<it id="2" pos="open"><T></it>. + + Use "Async" suffix in names of methods that return an awaitable type. + Use "Async" suffix in names of methods that return an awaitable type. + + + Avoid "Async" suffix in names of methods that do not return an awaitable type. + Avoid "Async" suffix in names of methods that do not return an awaitable type. + + + Add .ConfigureAwait(false) + Add .ConfigureAwait(false) + ".ConfigureAwait(false)" should not be translated. + + + Add .ConfigureAwait(true) + Add .ConfigureAwait(true) + ".ConfigureAwait(true)" should not be translated. + + + Add .ConfigureAwait(bool) to your await expression. + Add .ConfigureAwait(bool) to your await expression. + ".ConfigureAwait(bool)" and "await" should NOT be translated. + + + Use ConfigureAwait(bool) + Use ConfigureAwait(bool) + "ConfigureAwait(bool)" is a reference and should NOT be translated. + diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.it.xlf b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.it.xlf index 4b151081..15149c49 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.it.xlf +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.it.xlf @@ -1,4 +1,4 @@ - +
@@ -104,10 +104,6 @@ In alternativa, usare AsyncLazy<T>. Rinomina in {0} {0} is a method name. - - Use "Async" suffix in names of Task-returning methods. - Usare il suffisso "Async" in nomi di metodi che restituiscono Task. - Use "Async" suffix for async methods Usa suffisso "Async" per metodi asincroni @@ -219,6 +215,34 @@ Use AsyncLazy<T> instead. La chiamata o il blocco su codice asincrono in una factory di valori Lazy<it id="1" pos="open"><T></it> può causare un deadlock. In alternativa, usare AsyncLazy<it id="2" pos="open"><T></it>. + + Use "Async" suffix in names of methods that return an awaitable type. + Use "Async" suffix in names of methods that return an awaitable type. + + + Avoid "Async" suffix in names of methods that do not return an awaitable type. + Avoid "Async" suffix in names of methods that do not return an awaitable type. + + + Add .ConfigureAwait(false) + Add .ConfigureAwait(false) + ".ConfigureAwait(false)" should not be translated. + + + Add .ConfigureAwait(true) + Add .ConfigureAwait(true) + ".ConfigureAwait(true)" should not be translated. + + + Add .ConfigureAwait(bool) to your await expression. + Add .ConfigureAwait(bool) to your await expression. + ".ConfigureAwait(bool)" and "await" should NOT be translated. + + + Use ConfigureAwait(bool) + Use ConfigureAwait(bool) + "ConfigureAwait(bool)" is a reference and should NOT be translated. + diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.ja.xlf b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.ja.xlf index 5a155d6e..f1116b8b 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.ja.xlf +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.ja.xlf @@ -1,4 +1,4 @@ - +
@@ -104,10 +104,6 @@ Use AsyncLazy<T> instead. {0} に名前を変更する {0} is a method name. - - Use "Async" suffix in names of Task-returning methods. - タスクを返すメソッドの名前に "Async" サフィックスを使用します。 - Use "Async" suffix for async methods 非同期メソッドに "Async" サフィックスを使用する @@ -219,6 +215,34 @@ Use AsyncLazy<T> instead. Lazy<it id="1" pos="open"><T></it> 値ファクトリの非同期コードで呼び出しかブロックを実行すると、デッドロックを引き起こす可能性があります。 代わりに AsyncLazy<it id="2" pos="open"><T></it> をご使用ください。 + + Use "Async" suffix in names of methods that return an awaitable type. + Use "Async" suffix in names of methods that return an awaitable type. + + + Avoid "Async" suffix in names of methods that do not return an awaitable type. + Avoid "Async" suffix in names of methods that do not return an awaitable type. + + + Add .ConfigureAwait(false) + Add .ConfigureAwait(false) + ".ConfigureAwait(false)" should not be translated. + + + Add .ConfigureAwait(true) + Add .ConfigureAwait(true) + ".ConfigureAwait(true)" should not be translated. + + + Add .ConfigureAwait(bool) to your await expression. + Add .ConfigureAwait(bool) to your await expression. + ".ConfigureAwait(bool)" and "await" should NOT be translated. + + + Use ConfigureAwait(bool) + Use ConfigureAwait(bool) + "ConfigureAwait(bool)" is a reference and should NOT be translated. + diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.ko.xlf b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.ko.xlf index 0a84a195..418f36b4 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.ko.xlf +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.ko.xlf @@ -1,4 +1,4 @@ - +
@@ -104,10 +104,6 @@ Use AsyncLazy<T> instead. {0}(으)로 이름 바꾸기 {0} is a method name. - - Use "Async" suffix in names of Task-returning methods. - Task-returning 메서드의 이름에 "Async" 접미사를 사용합니다. - Use "Async" suffix for async methods 비동기 메서드에 "Async" 접미사 사용 @@ -219,6 +215,34 @@ Use AsyncLazy<T> instead. Lazy<it id="1" pos="open"><T></it> 값 팩터리의 비동기 코드를 호출하거나 차단하면 교착 상태가 될 수 있습니다. 대신 AsyncLazy<it id="2" pos="open"><T></it>를 사용합니다. + + Use "Async" suffix in names of methods that return an awaitable type. + Use "Async" suffix in names of methods that return an awaitable type. + + + Avoid "Async" suffix in names of methods that do not return an awaitable type. + Avoid "Async" suffix in names of methods that do not return an awaitable type. + + + Add .ConfigureAwait(false) + Add .ConfigureAwait(false) + ".ConfigureAwait(false)" should not be translated. + + + Add .ConfigureAwait(true) + Add .ConfigureAwait(true) + ".ConfigureAwait(true)" should not be translated. + + + Add .ConfigureAwait(bool) to your await expression. + Add .ConfigureAwait(bool) to your await expression. + ".ConfigureAwait(bool)" and "await" should NOT be translated. + + + Use ConfigureAwait(bool) + Use ConfigureAwait(bool) + "ConfigureAwait(bool)" is a reference and should NOT be translated. + diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.pl.xlf b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.pl.xlf index 156a418c..11118061 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.pl.xlf +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.pl.xlf @@ -1,4 +1,4 @@ - +
@@ -104,10 +104,6 @@ Zamiast tego używaj klasy AsyncLazy<T>. Zmień nazwę na: {0} {0} is a method name. - - Use "Async" suffix in names of Task-returning methods. - W nazwach metod zwracających obiekty Task używaj sufiksu „Async”. - Use "Async" suffix for async methods W przypadku metod asynchronicznych używaj sufiksu „Async” @@ -219,6 +215,34 @@ Use AsyncLazy<T> instead. Wywołanie kodu asynchronicznego lub zablokowanie na nim w fabryce wartości Lazy<it id="1" pos="open"><T></it> może spowodować zakleszczenie. Zamiast tego użyj elementu AsyncLazy<it id="2" pos="open"><T></it>. + + Use "Async" suffix in names of methods that return an awaitable type. + Use "Async" suffix in names of methods that return an awaitable type. + + + Avoid "Async" suffix in names of methods that do not return an awaitable type. + Avoid "Async" suffix in names of methods that do not return an awaitable type. + + + Add .ConfigureAwait(false) + Add .ConfigureAwait(false) + ".ConfigureAwait(false)" should not be translated. + + + Add .ConfigureAwait(true) + Add .ConfigureAwait(true) + ".ConfigureAwait(true)" should not be translated. + + + Add .ConfigureAwait(bool) to your await expression. + Add .ConfigureAwait(bool) to your await expression. + ".ConfigureAwait(bool)" and "await" should NOT be translated. + + + Use ConfigureAwait(bool) + Use ConfigureAwait(bool) + "ConfigureAwait(bool)" is a reference and should NOT be translated. + diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.pt-BR.xlf b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.pt-BR.xlf index bb28d62d..0b9c91ee 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.pt-BR.xlf +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.pt-BR.xlf @@ -1,4 +1,4 @@ - +
@@ -104,10 +104,6 @@ Em vez disso, use AsyncLazy<T>. Renomear para {0} {0} is a method name. - - Use "Async" suffix in names of Task-returning methods. - Use o sufixo "Async" em nomes de métodos que retornam tarefas. - Use "Async" suffix for async methods Use o sufixo "Async" para métodos assíncronos @@ -219,6 +215,34 @@ Use AsyncLazy<T> instead. A invocação ou bloqueio em código assíncrono em um alocador de valor Lazy<it id="1" pos="open"><T></it> pode causar um deadlock. Use AsyncLazy<it id="2" pos="open"><T></it> em vez disso. + + Use "Async" suffix in names of methods that return an awaitable type. + Use "Async" suffix in names of methods that return an awaitable type. + + + Avoid "Async" suffix in names of methods that do not return an awaitable type. + Avoid "Async" suffix in names of methods that do not return an awaitable type. + + + Add .ConfigureAwait(false) + Add .ConfigureAwait(false) + ".ConfigureAwait(false)" should not be translated. + + + Add .ConfigureAwait(true) + Add .ConfigureAwait(true) + ".ConfigureAwait(true)" should not be translated. + + + Add .ConfigureAwait(bool) to your await expression. + Add .ConfigureAwait(bool) to your await expression. + ".ConfigureAwait(bool)" and "await" should NOT be translated. + + + Use ConfigureAwait(bool) + Use ConfigureAwait(bool) + "ConfigureAwait(bool)" is a reference and should NOT be translated. + diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.ru.xlf b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.ru.xlf index c169be51..06227dc0 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.ru.xlf +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.ru.xlf @@ -1,4 +1,4 @@ - +
@@ -104,10 +104,6 @@ Use AsyncLazy<T> instead. Переименование в {0} {0} is a method name. - - Use "Async" suffix in names of Task-returning methods. - Используйте суффикс "Async" в названиях методов, возвращающих Task. - Use "Async" suffix for async methods Использование суффикса "Async" в асинхронных методах @@ -219,6 +215,34 @@ Use AsyncLazy<T> instead. Вызов или блокировка в асинхронном коде в методе ValueFactory Lazy<it id="1" pos="open"><T></it> может вызвать взаимоблокировку. Вместо этого используйте AsyncLazy<it id="2" pos="open"><T></it>. + + Use "Async" suffix in names of methods that return an awaitable type. + Use "Async" suffix in names of methods that return an awaitable type. + + + Avoid "Async" suffix in names of methods that do not return an awaitable type. + Avoid "Async" suffix in names of methods that do not return an awaitable type. + + + Add .ConfigureAwait(false) + Add .ConfigureAwait(false) + ".ConfigureAwait(false)" should not be translated. + + + Add .ConfigureAwait(true) + Add .ConfigureAwait(true) + ".ConfigureAwait(true)" should not be translated. + + + Add .ConfigureAwait(bool) to your await expression. + Add .ConfigureAwait(bool) to your await expression. + ".ConfigureAwait(bool)" and "await" should NOT be translated. + + + Use ConfigureAwait(bool) + Use ConfigureAwait(bool) + "ConfigureAwait(bool)" is a reference and should NOT be translated. + diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.tr.xlf b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.tr.xlf index a36f8f12..c4491a45 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.tr.xlf +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.tr.xlf @@ -1,4 +1,4 @@ - +
@@ -104,10 +104,6 @@ Bunun yerine AsyncLazy<T> kullanın. {0} olarak yeniden adlandırın {0} is a method name. - - Use "Async" suffix in names of Task-returning methods. - Task döndüren metotların adlarında “Async” kullanın. - Use "Async" suffix for async methods Async metotları için "Async" sonekini kullanın @@ -219,6 +215,34 @@ Use AsyncLazy<T> instead. Bir Lazy<it id="1" pos="open"><T></it> değer fabrikasında zaman uyumsuz kod çağrısı veya engellemesi çıkmaza yol açabilir. Bunun yerine AsyncLazy<it id="2" pos="open"><T></it> kullanın. + + Use "Async" suffix in names of methods that return an awaitable type. + Use "Async" suffix in names of methods that return an awaitable type. + + + Avoid "Async" suffix in names of methods that do not return an awaitable type. + Avoid "Async" suffix in names of methods that do not return an awaitable type. + + + Add .ConfigureAwait(false) + Add .ConfigureAwait(false) + ".ConfigureAwait(false)" should not be translated. + + + Add .ConfigureAwait(true) + Add .ConfigureAwait(true) + ".ConfigureAwait(true)" should not be translated. + + + Add .ConfigureAwait(bool) to your await expression. + Add .ConfigureAwait(bool) to your await expression. + ".ConfigureAwait(bool)" and "await" should NOT be translated. + + + Use ConfigureAwait(bool) + Use ConfigureAwait(bool) + "ConfigureAwait(bool)" is a reference and should NOT be translated. + diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.zh-Hans.xlf b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.zh-Hans.xlf index bd2790eb..0b2a1410 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.zh-Hans.xlf +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.zh-Hans.xlf @@ -1,4 +1,4 @@ - +
@@ -104,10 +104,6 @@ Use AsyncLazy<T> instead. 重命名为 {0} {0} is a method name. - - Use "Async" suffix in names of Task-returning methods. - 在返回 Task 的方法的名称中使用“Async”后缀。 - Use "Async" suffix for async methods 对异步方法使用“Async”后缀 @@ -218,6 +214,34 @@ Use AsyncLazy<T> instead. Use AsyncLazy<T> instead. 在 Lazy<it id="1" pos="open"><T></it> 值工厂中调用或阻止异步代码可能死锁。 请改用 AsyncLazy<it id="2" pos="open"><T></it> + + Use "Async" suffix in names of methods that return an awaitable type. + Use "Async" suffix in names of methods that return an awaitable type. + + + Avoid "Async" suffix in names of methods that do not return an awaitable type. + Avoid "Async" suffix in names of methods that do not return an awaitable type. + + + Add .ConfigureAwait(false) + Add .ConfigureAwait(false) + ".ConfigureAwait(false)" should not be translated. + + + Add .ConfigureAwait(true) + Add .ConfigureAwait(true) + ".ConfigureAwait(true)" should not be translated. + + + Add .ConfigureAwait(bool) to your await expression. + Add .ConfigureAwait(bool) to your await expression. + ".ConfigureAwait(bool)" and "await" should NOT be translated. + + + Use ConfigureAwait(bool) + Use ConfigureAwait(bool) + "ConfigureAwait(bool)" is a reference and should NOT be translated. + diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.zh-Hant.xlf b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.zh-Hant.xlf index 6fd4eefc..5f73826b 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.zh-Hant.xlf +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/MultilingualResources/Microsoft.VisualStudio.Threading.Analyzers.zh-Hant.xlf @@ -1,4 +1,4 @@ - +
@@ -104,10 +104,6 @@ Use AsyncLazy<T> instead. 重新命名為 {0} {0} is a method name. - - Use "Async" suffix in names of Task-returning methods. - 請在 Task 傳回方法的名稱中使用 "Async" 尾碼。 - Use "Async" suffix for async methods 對非同步方法使用 "Async" 尾碼 @@ -219,6 +215,34 @@ Use AsyncLazy<T> instead. 在 Lazy<it id="1" pos="open"><T></it> 值 Factory 中叫用或封鎖非同步程式碼可能會鎖死。 請改用 AsyncLazy<it id="2" pos="open"><T></it>。 + + Use "Async" suffix in names of methods that return an awaitable type. + Use "Async" suffix in names of methods that return an awaitable type. + + + Avoid "Async" suffix in names of methods that do not return an awaitable type. + Avoid "Async" suffix in names of methods that do not return an awaitable type. + + + Add .ConfigureAwait(false) + Add .ConfigureAwait(false) + ".ConfigureAwait(false)" should not be translated. + + + Add .ConfigureAwait(true) + Add .ConfigureAwait(true) + ".ConfigureAwait(true)" should not be translated. + + + Add .ConfigureAwait(bool) to your await expression. + Add .ConfigureAwait(bool) to your await expression. + ".ConfigureAwait(bool)" and "await" should NOT be translated. + + + Use ConfigureAwait(bool) + Use ConfigureAwait(bool) + "ConfigureAwait(bool)" is a reference and should NOT be translated. + diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.Designer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.Designer.cs index 79fd214f..7dc63d87 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.Designer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.Designer.cs @@ -460,6 +460,51 @@ namespace Microsoft.VisualStudio.Threading.Analyzers { } } + /// + /// Looks up a localized string similar to Add .ConfigureAwait(false). + /// + internal static string VSTHRD111_CodeFix_False_Title { + get { + return ResourceManager.GetString("VSTHRD111_CodeFix_False_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add .ConfigureAwait(true). + /// + internal static string VSTHRD111_CodeFix_True_Title { + get { + return ResourceManager.GetString("VSTHRD111_CodeFix_True_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add .ConfigureAwait(bool) to your await expression.. + /// + internal static string VSTHRD111_MessageFormat { + get { + return ResourceManager.GetString("VSTHRD111_MessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use ConfigureAwait(bool). + /// + internal static string VSTHRD111_Title { + get { + return ResourceManager.GetString("VSTHRD111_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use "Async" suffix in names of methods that return an awaitable type.. + /// + internal static string VSTHRD200_AddAsync_MessageFormat { + get { + return ResourceManager.GetString("VSTHRD200_AddAsync_MessageFormat", resourceCulture); + } + } + /// /// Looks up a localized string similar to Rename to {0}. /// @@ -470,11 +515,11 @@ namespace Microsoft.VisualStudio.Threading.Analyzers { } /// - /// Looks up a localized string similar to Use "Async" suffix in names of Task-returning methods.. + /// Looks up a localized string similar to Avoid "Async" suffix in names of methods that do not return an awaitable type.. /// - internal static string VSTHRD200_MessageFormat { + internal static string VSTHRD200_RemoveAsync_MessageFormat { get { - return ResourceManager.GetString("VSTHRD200_MessageFormat", resourceCulture); + return ResourceManager.GetString("VSTHRD200_RemoveAsync_MessageFormat", resourceCulture); } } diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.cs.resx b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.cs.resx index 1239bccb..cb4f84ed 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.cs.resx +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.cs.resx @@ -85,9 +85,6 @@ Použijte raději AsyncLazy<T>. Přejmenování na {0} {0} is a method name. - - Příponu Async použijte v názvech metod, které vrací Task. - Použití přípony Async pro asynchronní metody diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.de.resx b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.de.resx index 3b3cdc99..04d31f10 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.de.resx +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.de.resx @@ -85,9 +85,6 @@ Verwenden Sie stattdessen "AsyncLazy<T>". In {0} umbenennen {0} is a method name. - - Verwenden Sie das Suffix "Async" in Namen von Methoden, die Tasks zurückgeben. - Verwenden Sie das Suffix "Async" für asynchrone Methoden diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.es.resx b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.es.resx index 02251fba..49558396 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.es.resx +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.es.resx @@ -85,9 +85,6 @@ Use AsyncLazy<T> en su lugar. Cambie el nombre a {0} {0} is a method name. - - Use el sufijo "Async" en nombres de métodos de devolución de elementos Task - Use el sufijo "Async" para métodos asincrónicos diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.fr.resx b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.fr.resx index 71121dad..33e66593 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.fr.resx +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.fr.resx @@ -85,9 +85,6 @@ Utilisez AsyncLazy<T> à la place Renommer en {0} {0} is a method name. - - Utilisez le suffixe "Async" dans les noms des méthodes retournant Task. - Utiliser le suffixe "Async" pour les méthodes asynchrones diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.it.resx b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.it.resx index f33494fc..364d3b8b 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.it.resx +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.it.resx @@ -85,9 +85,6 @@ In alternativa, usare AsyncLazy<T>. Rinomina in {0} {0} is a method name. - - Usare il suffisso "Async" in nomi di metodi che restituiscono Task. - Usa suffisso "Async" per metodi asincroni diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.ja.resx b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.ja.resx index 6083008b..d0b0781a 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.ja.resx +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.ja.resx @@ -85,9 +85,6 @@ {0} に名前を変更する {0} is a method name. - - タスクを返すメソッドの名前に "Async" サフィックスを使用します。 - 非同期メソッドに "Async" サフィックスを使用する diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.ko.resx b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.ko.resx index ffc39b70..4ee3cc09 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.ko.resx +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.ko.resx @@ -85,9 +85,6 @@ {0}(으)로 이름 바꾸기 {0} is a method name. - - Task-returning 메서드의 이름에 "Async" 접미사를 사용합니다. - 비동기 메서드에 "Async" 접미사 사용 diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.pl.resx b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.pl.resx index 230e0b42..4350f64d 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.pl.resx +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.pl.resx @@ -85,9 +85,6 @@ Zamiast tego używaj klasy AsyncLazy<T>. Zmień nazwę na: {0} {0} is a method name. - - W nazwach metod zwracających obiekty Task używaj sufiksu „Async”. - W przypadku metod asynchronicznych używaj sufiksu „Async” diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.pt-BR.resx b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.pt-BR.resx index 70b562de..428b6114 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.pt-BR.resx +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.pt-BR.resx @@ -85,9 +85,6 @@ Em vez disso, use AsyncLazy<T>. Renomear para {0} {0} is a method name. - - Use o sufixo "Async" em nomes de métodos que retornam tarefas. - Use o sufixo "Async" para métodos assíncronos diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.resx b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.resx index 53c24a1c..7e5d4d22 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.resx +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.resx @@ -202,8 +202,8 @@ Use AsyncLazy<T> instead. Rename to {0} {0} is a method name. - - Use "Async" suffix in names of Task-returning methods. + + Use "Async" suffix in names of methods that return an awaitable type. Use "Async" suffix for async methods @@ -278,4 +278,23 @@ Use AsyncLazy<T> instead. Observe result of async calls + + Avoid "Async" suffix in names of methods that do not return an awaitable type. + + + Add .ConfigureAwait(false) + ".ConfigureAwait(false)" should not be translated. + + + Add .ConfigureAwait(true) + ".ConfigureAwait(true)" should not be translated. + + + Add .ConfigureAwait(bool) to your await expression. + ".ConfigureAwait(bool)" and "await" should NOT be translated. + + + Use ConfigureAwait(bool) + "ConfigureAwait(bool)" is a reference and should NOT be translated. + \ No newline at end of file diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.ru.resx b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.ru.resx index 4a013490..f2338336 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.ru.resx +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.ru.resx @@ -85,9 +85,6 @@ Переименование в {0} {0} is a method name. - - Используйте суффикс "Async" в названиях методов, возвращающих Task. - Использование суффикса "Async" в асинхронных методах diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.tr.resx b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.tr.resx index 6a85f534..c11313f5 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.tr.resx +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.tr.resx @@ -85,9 +85,6 @@ Bunun yerine AsyncLazy<T> kullanın. {0} olarak yeniden adlandırın {0} is a method name. - - Task döndüren metotların adlarında “Async” kullanın. - Async metotları için "Async" sonekini kullanın diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.zh-Hans.resx b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.zh-Hans.resx index 2bee7e5c..d418aefb 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.zh-Hans.resx +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.zh-Hans.resx @@ -85,9 +85,6 @@ 重命名为 {0} {0} is a method name. - - 在返回 Task 的方法的名称中使用“Async”后缀。 - 对异步方法使用“Async”后缀 diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.zh-Hant.resx b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.zh-Hant.resx index 7cdc96d2..507e9636 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.zh-Hant.resx +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/Strings.zh-Hant.resx @@ -85,9 +85,6 @@ 重新命名為 {0} {0} is a method name. - - 請在 Task 傳回方法的名稱中使用 "Async" 尾碼。 - 對非同步方法使用 "Async" 尾碼 diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Types.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/Types.cs index 98d8d7ca..de42e43f 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/Types.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/Types.cs @@ -146,6 +146,15 @@ namespace Microsoft.VisualStudio.Threading.Analyzers internal static readonly IReadOnlyList Namespace = Namespaces.SystemThreadingTasks; } + internal static class ValueTask + { + internal const string TypeName = "ValueTask"; + + internal const string FullName = "System.Threading.Tasks." + TypeName; + + internal static readonly IReadOnlyList Namespace = Namespaces.SystemThreadingTasks; + } + internal static class CoClassAttribute { internal const string TypeName = nameof(System.Runtime.InteropServices.CoClassAttribute); diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/Utils.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/Utils.cs index ab35a696..2846b794 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/Utils.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/Utils.cs @@ -733,6 +733,33 @@ namespace Microsoft.VisualStudio.Threading.Analyzers return SyntaxFactory.QualifiedName(result, simpleName); } + internal static MemberAccessExpressionSyntax MemberAccess(IReadOnlyList qualifiers, SimpleNameSyntax simpleName) + { + if (qualifiers == null) + { + throw new ArgumentNullException(nameof(qualifiers)); + } + + if (simpleName == null) + { + throw new ArgumentNullException(nameof(simpleName)); + } + + if (qualifiers.Count == 0) + { + throw new ArgumentException("At least one qualifier required."); + } + + ExpressionSyntax result = SyntaxFactory.IdentifierName(qualifiers[0]); + for (int i = 1; i < qualifiers.Count; i++) + { + var rightSide = SyntaxFactory.IdentifierName(qualifiers[i]); + result = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, result, rightSide); + } + + return SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, result, simpleName); + } + internal static string GetFullName(ISymbol symbol) { if (symbol == null) diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD010MainThreadUsageCodeFix.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD010MainThreadUsageCodeFix.cs index 3102b78b..6e753d32 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD010MainThreadUsageCodeFix.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD010MainThreadUsageCodeFix.cs @@ -144,7 +144,10 @@ Task Fix(string fullyQualifiedMethod, IMethodSymbol methodSymbol, Lazy cancellationTokenSymbol, CancellationToken cancellationToken) { - var invocationExpression = SyntaxFactory.InvocationExpression(SyntaxFactory.ParseName(fullyQualifiedMethod)); + int typeAndMethodDelimiterIndex = fullyQualifiedMethod.LastIndexOf('.'); + IdentifierNameSyntax methodName = SyntaxFactory.IdentifierName(fullyQualifiedMethod.Substring(typeAndMethodDelimiterIndex + 1)); + ExpressionSyntax invokedMethod = Utils.MemberAccess(fullyQualifiedMethod.Substring(0, typeAndMethodDelimiterIndex).Split('.'), methodName); + var invocationExpression = SyntaxFactory.InvocationExpression(invokedMethod); var cancellationTokenParameter = methodSymbol.Parameters.FirstOrDefault(IsCancellationTokenParameter); if (cancellationTokenParameter != null && cancellationTokenSymbol.Value != null) { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD111UseConfigureAwaitAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD111UseConfigureAwaitAnalyzer.cs new file mode 100644 index 00000000..d2a4e04d --- /dev/null +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD111UseConfigureAwaitAnalyzer.cs @@ -0,0 +1,56 @@ +namespace Microsoft.VisualStudio.Threading.Analyzers +{ + using System; + using System.Collections.Immutable; + using System.Threading.Tasks; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp; + using Microsoft.CodeAnalysis.CSharp.Syntax; + using Microsoft.CodeAnalysis.Diagnostics; + + /// + /// Finds await expressions on that do not use . + /// Also works on ValueTask. + /// + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class VSTHRD111UseConfigureAwaitAnalyzer : DiagnosticAnalyzer + { + public const string Id = "VSTHRD111"; + + internal static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor( + id: Id, + title: Strings.VSTHRD111_Title, + messageFormat: Strings.VSTHRD111_MessageFormat, + helpLinkUri: Utils.GetHelpLink(Id), + category: "Usage", + defaultSeverity: DiagnosticSeverity.Hidden, // projects should opt IN to this policy + isEnabledByDefault: true); + + /// + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Descriptor); + + /// + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze); + + context.RegisterSyntaxNodeAction(Utils.DebuggableWrapper(this.AnalyzeAwaitExpression), SyntaxKind.AwaitExpression); + } + + private void AnalyzeAwaitExpression(SyntaxNodeAnalysisContext context) + { + var awaitExpression = (AwaitExpressionSyntax)context.Node; + + // Emit the diagnostic if the awaited expression is a Task or ValueTask. + // They obviously aren't using ConfigureAwait in that case since the awaited expression type would be a + // ConfiguredTaskAwaitable instead. + var awaitedTypeInfo = context.SemanticModel.GetTypeInfo(awaitExpression.Expression, context.CancellationToken); + if (awaitedTypeInfo.Type != null && awaitedTypeInfo.Type.BelongsToNamespace(Namespaces.SystemThreadingTasks) && + (awaitedTypeInfo.Type.Name == Types.Task.TypeName || awaitedTypeInfo.Type.Name == Types.ValueTask.TypeName)) + { + context.ReportDiagnostic(Diagnostic.Create(Descriptor, awaitExpression.Expression.GetLocation())); + } + } + } +} diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD111UseConfigureAwaitCodeFix.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD111UseConfigureAwaitCodeFix.cs new file mode 100644 index 00000000..d15adde0 --- /dev/null +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD111UseConfigureAwaitCodeFix.cs @@ -0,0 +1,58 @@ +namespace Microsoft.VisualStudio.Threading.Analyzers +{ + using System; + using System.Collections.Generic; + using System.Collections.Immutable; + using System.Globalization; + using System.Linq; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CodeActions; + using Microsoft.CodeAnalysis.CodeFixes; + using Microsoft.CodeAnalysis.CSharp; + using Microsoft.CodeAnalysis.CSharp.Syntax; + using Microsoft.CodeAnalysis.Simplification; + using Microsoft.VisualStudio.Threading; + + [ExportCodeFixProvider(LanguageNames.CSharp)] + public class VSTHRD111UseConfigureAwaitCodeFix : CodeFixProvider + { + private static readonly ImmutableArray ReusableFixableDiagnosticIds = ImmutableArray.Create( + VSTHRD111UseConfigureAwaitAnalyzer.Id); + + /// + public override ImmutableArray FixableDiagnosticIds => ReusableFixableDiagnosticIds; + + /// + public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + foreach (var 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 awaitedExpression = syntaxRoot.FindNode(diagnostic.Location.SourceSpan) as ExpressionSyntax; + + Task ApplyFix(bool captureContext, CancellationToken cancellationToken) + { + ExpressionSyntax configuredAwaitExpression = SyntaxFactory.ParenthesizedExpression( + SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.ParenthesizedExpression(awaitedExpression).WithAdditionalAnnotations(Simplifier.Annotation), + SyntaxFactory.IdentifierName("ConfigureAwait"))) + .AddArgumentListArguments(SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(captureContext ? SyntaxKind.TrueLiteralExpression : SyntaxKind.FalseLiteralExpression)))) + .WithAdditionalAnnotations(Simplifier.Annotation); + + 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); + } + } + } +} diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD200UseAsyncNamingConventionAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD200UseAsyncNamingConventionAnalyzer.cs index b3b50f8e..4dfc615e 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD200UseAsyncNamingConventionAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD200UseAsyncNamingConventionAnalyzer.cs @@ -22,10 +22,19 @@ namespace Microsoft.VisualStudio.Threading.Analyzers internal const string MandatoryAsyncSuffix = "Async"; - internal static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor( + internal static readonly DiagnosticDescriptor AddAsyncDescriptor = new DiagnosticDescriptor( id: Id, title: Strings.VSTHRD200_Title, - messageFormat: Strings.VSTHRD200_MessageFormat, + messageFormat: Strings.VSTHRD200_AddAsync_MessageFormat, + helpLinkUri: Utils.GetHelpLink(Id), + category: "Style", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + internal static readonly DiagnosticDescriptor RemoveAsyncDescriptor = new DiagnosticDescriptor( + id: Id, + title: Strings.VSTHRD200_Title, + messageFormat: Strings.VSTHRD200_RemoveAsync_MessageFormat, helpLinkUri: Utils.GetHelpLink(Id), category: "Style", defaultSeverity: DiagnosticSeverity.Warning, @@ -33,7 +42,8 @@ namespace Microsoft.VisualStudio.Threading.Analyzers /// public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create( - Descriptor); + AddAsyncDescriptor, + RemoveAsyncDescriptor); /// public override void Initialize(AnalysisContext context) @@ -53,28 +63,38 @@ namespace Microsoft.VisualStudio.Threading.Analyzers return; } - if (!methodSymbol.Name.EndsWith(MandatoryAsyncSuffix)) + bool shouldEndWithAsync = (methodSymbol.ReturnType.Name == nameof(Task) || methodSymbol.ReturnType.Name == "ValueTask") && + methodSymbol.ReturnType.BelongsToNamespace(Namespaces.SystemThreadingTasks); + + // Skip entrypoint methods since they must be called Main. + shouldEndWithAsync &= !Utils.IsEntrypointMethod(methodSymbol, context.Compilation, context.CancellationToken); + + bool actuallyEndsWithAsync = methodSymbol.Name.EndsWith(MandatoryAsyncSuffix); + + if (shouldEndWithAsync != actuallyEndsWithAsync) { - if (methodSymbol.ReturnType.Name == nameof(Task) && - methodSymbol.ReturnType.BelongsToNamespace(Namespaces.SystemThreadingTasks)) + // Now that we have done the cheap checks to find that this method may deserve a diagnostic, + // Do deeper checks to skip over methods that implement API contracts that are controlled elsewhere. + if (methodSymbol.FindInterfacesImplemented().Any() || methodSymbol.IsOverride) { - // Skip entrypoint methods since they must be called Main. - if (Utils.IsEntrypointMethod(methodSymbol, context.Compilation, context.CancellationToken)) - { - return; - } - - // Now that we have done the cheap checks to find that this method may deserve a diagnostic, - // Do deeper checks to skip over methods that implement API contracts that are controlled elsewhere. - if (methodSymbol.FindInterfacesImplemented().Any() || methodSymbol.IsOverride) - { - return; - } + return; + } + if (shouldEndWithAsync) + { var properties = ImmutableDictionary.Empty .Add(VSTHRD200UseAsyncNamingConventionCodeFix.NewNameKey, methodSymbol.Name + MandatoryAsyncSuffix); context.ReportDiagnostic(Diagnostic.Create( - Descriptor, + AddAsyncDescriptor, + methodSymbol.Locations[0], + properties)); + } + else + { + var properties = ImmutableDictionary.Empty + .Add(VSTHRD200UseAsyncNamingConventionCodeFix.NewNameKey, methodSymbol.Name.Substring(0, methodSymbol.Name.Length - MandatoryAsyncSuffix.Length)); + context.ReportDiagnostic(Diagnostic.Create( + RemoveAsyncDescriptor, methodSymbol.Locations[0], properties)); } diff --git a/src/Microsoft.VisualStudio.Threading.Tests/AsyncAutoResetEventTests.cs b/src/Microsoft.VisualStudio.Threading.Tests/AsyncAutoResetEventTests.cs index 83883278..7a552259 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/AsyncAutoResetEventTests.cs +++ b/src/Microsoft.VisualStudio.Threading.Tests/AsyncAutoResetEventTests.cs @@ -81,7 +81,7 @@ TaskContinuationOptions.ExecuteSynchronously); this.evt.Set(); setReturned.Set(); - Assert.True(inlinedContinuation.Wait(AsyncDelay)); + Assert.True(inlinedContinuation.Wait(ExpectedTimeout)); } /// diff --git a/src/Microsoft.VisualStudio.Threading.Tests/AsyncReaderWriterLockTests.cs b/src/Microsoft.VisualStudio.Threading.Tests/AsyncReaderWriterLockTests.cs index f3f78db5..b0ce39d9 100644 --- a/src/Microsoft.VisualStudio.Threading.Tests/AsyncReaderWriterLockTests.cs +++ b/src/Microsoft.VisualStudio.Threading.Tests/AsyncReaderWriterLockTests.cs @@ -1450,7 +1450,7 @@ }).GetAwaiter().GetResult(); } - [StaFact] + [StaFact, Trait("TestCategory", "FailsInCloudTest")] public void UpgradeableReadLockAsyncSynchronousReleaseAllowsOtherUpgradeableReaders() { var testComplete = new ManualResetEventSlim(); // deliberately synchronous diff --git a/src/global.json b/src/global.json new file mode 100644 index 00000000..d65e3d5d --- /dev/null +++ b/src/global.json @@ -0,0 +1,5 @@ +{ + "sdk": { + "version": "2.1.300" + } +} diff --git a/nuget.config b/src/nuget.config similarity index 91% rename from nuget.config rename to src/nuget.config index 00dc95a0..a17ebe35 100644 --- a/nuget.config +++ b/src/nuget.config @@ -1,7 +1,7 @@  - + diff --git a/src/version.json b/src/version.json index e71a7244..5e944012 100644 --- a/src/version.json +++ b/src/version.json @@ -4,10 +4,5 @@ "publicReleaseRefSpec": [ "^refs/heads/master$", // we release out of master "^refs/heads/v\\d+(?:\\.\\d+)?$" // we also release out of vNN branches - ], - "cloudBuild": { - "buildNumber": { - "enabled": true - } - } + ] }