diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..fc10450 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,168 @@ +# EditorConfig helps developers maintain consistent coding styles across editors and IDEs +# Docs: https://editorconfig.org/ + +root = true + +# ======================================== +# XML, MSBuild files +# ======================================== +[*.xml] +indent_style = space +indent_size = 2 +insert_final_newline = true +resharper_enforce_empty_line_at_end_of_file = true +trim_trailing_whitespace = true + +[*.resx] +indent_style = space +indent_size = 2 +insert_final_newline = true +resharper_enforce_empty_line_at_end_of_file = true +trim_trailing_whitespace = true + +[*.config] +indent_style = space +indent_size = 2 +insert_final_newline = true +resharper_enforce_empty_line_at_end_of_file = true +trim_trailing_whitespace = true + +[*.csproj] +indent_style = space +indent_size = 2 +insert_final_newline = true +resharper_enforce_empty_line_at_end_of_file = true +trim_trailing_whitespace = true + +[*.*proj] +indent_style = space +indent_size = 2 +insert_final_newline = true +resharper_enforce_empty_line_at_end_of_file = true +trim_trailing_whitespace = true + +[*.props] +indent_style = space +indent_size = 2 +insert_final_newline = true +resharper_enforce_empty_line_at_end_of_file = true +trim_trailing_whitespace = true + +[*.targets] +indent_style = space +indent_size = 2 +insert_final_newline = true +resharper_enforce_empty_line_at_end_of_file = true +trim_trailing_whitespace = true + +# ======================================== +# JSON files +# ======================================== +[*.json] +indent_style = space +indent_size = 2 +insert_final_newline = true +resharper_enforce_empty_line_at_end_of_file = true +trim_trailing_whitespace = true + +# ======================================== +# Markdown +# ======================================== +[*.md] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = false +insert_final_newline = true +resharper_enforce_empty_line_at_end_of_file = true + +# ======================================== +# YAML +# ======================================== +[*.yml] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = false +insert_final_newline = true +resharper_enforce_empty_line_at_end_of_file = true + +[*.yaml] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = false +insert_final_newline = true +resharper_enforce_empty_line_at_end_of_file = true + +# ======================================== +# C# files +# ======================================== +[*.cs] +indent_style = space +indent_size = 4 +insert_final_newline = true +resharper_enforce_empty_line_at_end_of_file = true +trim_trailing_whitespace = true + +# ======================================== +# Solution files +# ======================================== +[*.sln] +indent_style = tab +tab_width = 4 +insert_final_newline = true +resharper_enforce_empty_line_at_end_of_file = true +trim_trailing_whitespace = true + +# ======================================== +# Windows Batch and Command files +# ======================================== +[*.bat] +indent_style = space +indent_size = 4 +insert_final_newline = true +resharper_enforce_empty_line_at_end_of_file = true +trim_trailing_whitespace = false +end_of_line = crlf + +[*.cmd] +indent_style = space +indent_size = 4 +insert_final_newline = true +resharper_enforce_empty_line_at_end_of_file = true +trim_trailing_whitespace = false +end_of_line = crlf + +# ======================================== +# Powershell +# ======================================== +[*.ps1] +indent_style = space +indent_size = 4 +trim_trailing_whitespace = false +insert_final_newline = true +resharper_enforce_empty_line_at_end_of_file = true + +[*.psd1] +indent_style = space +indent_size = 4 +trim_trailing_whitespace = false +insert_final_newline = true +resharper_enforce_empty_line_at_end_of_file = true + +# ======================================== +# Bash +# ======================================== +[*.sh] +indent_style = space +indent_size = 4 +trim_trailing_whitespace = false +insert_final_newline = true +resharper_enforce_empty_line_at_end_of_file = true +end_of_line = lf + +[eng/git-hooks/*] +indent_style = space +indent_size = 4 +trim_trailing_whitespace = false +insert_final_newline = true +resharper_enforce_empty_line_at_end_of_file = true +end_of_line = lf \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..4947c44 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,37 @@ +# Let Git handle line endings automatically: +# - Checkout as CRLF on Windows, LF elsewhere +* text=auto + +# Explicit overrides for certain file types + +# C# code and projects +*.cs text eol=crlf diff=csharp +*.sln text merge=union eol=crlf +*.csproj text merge=union eol=lf +*.vbproj text merge=union eol=lf +*.fsproj text merge=union eol=lf + +# XML-based config/resources (store as LF in repo) +*.xml text eol=lf +*.resx text eol=lf +*.config text eol=lf +*.props text eol=lf +*.targets text eol=lf + +# JSON, YAML, Markdown +*.json text eol=lf +*.yml text eol=lf +*.yaml text eol=lf +*.md text eol=lf + +# Git metadata +.gitattributes text eol=lf +.gitignore text eol=lf + +# Ensure shell scripts always use LF +*.sh text eol=lf +eng/git-hooks/* text eol=lf + +# Ensure batch scripts always use CRLF +*.bat eol=crlf +*.cmd eol=crlf diff --git a/.github/.github.folderproj b/.github/.github.folderproj new file mode 100644 index 0000000..8933ebd --- /dev/null +++ b/.github/.github.folderproj @@ -0,0 +1,25 @@ + + + + + + diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000..3f9a332 --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,45 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +changelog: + exclude: + labels: + - notes:ignore + authors: + - dependabot + categories: + - title: 💥 Breaking Changes + labels: + - notes:breaking-change + - title: 🎉 New Features + labels: + - notes:new-feature + - title: 🐞 Bug Fixes + labels: + - notes:bug-fix + - title: 🚀 Performance Improvements + labels: + - notes:performance-improvement + - title: 🏆 Improvements + labels: + - notes:improvement + - title: 📄 Website and API Documentation + labels: + - notes:website-or-documentation + - title: 💪 Other Changes + labels: + - "*" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..8fff969 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,440 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: CI + +on: + workflow_dispatch: + push: + branches: + - main + - 'release/v*.*' # matches release/v1.2 + - 'release/v*.*.*' # matches release/v1.2.3 + - 'release-workflow*' # special branch names for testing release workflow (this file) from a PR + tags: + - 'v*' + pull_request: + release: + types: [published] + +jobs: + build: + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'pull_request' || + (github.event_name == 'push' && + (startsWith(github.ref, 'refs/heads/main') || + startsWith(github.ref, 'refs/heads/releases/v') || + github.ref_type == 'tag')) + runs-on: ubuntu-latest + env: + DIST_DIR: ${{ github.workspace }}/dist + NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages + steps: + - name: Checkout Source + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + with: + fetch-depth: 0 + + - name: Setup .NET 8.0 + uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5 + with: + dotnet-version: 8.0.x + + - name: Cache NuGet Packages + uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + with: + # '**/*.*proj' includes .csproj, .vbproj, .fsproj, msbuildproj, etc. + # '**/*.props' includes Directory.Packages.props and Directory.Build.props + # '**/*.targets' includes Directory.Build.targets + # '**/*.sln' and '*.sln' ensure root solution files are included (minimatch glitch for file extension .sln) + # 'global.json' included for SDK version changes + key: nuget-v1-${{ runner.os }}-${{ hashFiles('**/*.*proj', '**/*.props', '**/*.targets', '**/*.sln', '*.sln', 'global.json') }} + path: ${{ env.NUGET_PACKAGES }} + + - name: Restore + run: dotnet restore + + - name: Build + shell: pwsh + run: | + $nugetOut = Join-Path $env:DIST_DIR 'nuget' + dotnet build -c Release --no-restore -p:PackageOutputPath=$nugetOut + + - name: Publish Test Assemblies + shell: pwsh + run: | + $testsOut = Join-Path $env:DIST_DIR 'testBinaries' + New-Item -ItemType Directory -Force -Path $testsOut | Out-Null + + # Find all csproj files where a segment is exactly 'Tests' + Get-ChildItem -Recurse -Filter *.csproj | ForEach-Object { + Write-Host "Found project: $($_.FullName)" + $segments = $_.BaseName -split '\.' + if ($segments -contains 'Tests') { + $projName = $_.BaseName + $outDir = Join-Path $testsOut $projName + New-Item -ItemType Directory -Force -Path $outDir | Out-Null + Write-Host "Publishing $($_.FullName) -> $outDir" + dotnet publish $_.FullName -c Release --no-build -o $outDir --verbosity normal + if ($LASTEXITCODE -ne 0) { throw "Publish failed for $($_.FullName)" } + } + } + + - name: Upload NuGet packages + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: nuget-packages + path: ${{ env.DIST_DIR }}/nuget + + - name: Upload Test Assemblies + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: test-assemblies + path: ${{ env.DIST_DIR }}/testBinaries + + test: + needs: build + strategy: + matrix: + # macos-13 is specifically for running x64, macos-latest for arm64 + os: [windows-latest, ubuntu-latest, macos-13, macos-latest] + arch: [x64, arm64] + tfm: [net8.0] + exclude: + - arch: arm64 + os: macos-13 + - arch: arm64 + os: windows-latest + - arch: arm64 + os: ubuntu-latest + - arch: x64 + os: macos-latest + fail-fast: false + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout Source + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + + # Set up working directories/paths consistently + - name: Set Paths (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + $dir = "C:\w" + $testResultsRootDir = "$dir\work\test-results\${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.tfm }}" + $coverageReportDir = "$testResultsRootDir\coverage-report" + New-Item -ItemType Directory -Force -Path $dir + New-Item -ItemType Directory -Force -Path "$dir\dotnet" + New-Item -ItemType Directory -Force -Path "$dir\work" + New-Item -ItemType Directory -Force -Path "$testResultsRootDir" + New-Item -ItemType Directory -Force -Path "$coverageReportDir" + Add-Content $env:GITHUB_ENV "`nDOTNET_INSTALL_DIR=$dir\dotnet" + Add-Content $env:GITHUB_ENV "`nWORKPATH=$dir\work" + Add-Content $env:GITHUB_ENV "`nTEST_RESULTS_ROOT_DIR=$testResultsRootDir" + Add-Content $env:GITHUB_ENV "`nCOVERAGE_REPORT_DIR=$coverageReportDir" + Add-Content $env:GITHUB_PATH "`n$($env:USERPROFILE)\.dotnet\tools" + + - name: Set Paths (Linux/macOS) + if: runner.os == 'Linux' || runner.os == 'macOS' + shell: pwsh + run: | + $dir = "$env:RUNNER_TEMP/w" + $testResultsRootDir = "$dir/work/test-results/${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.tfm }}" + $coverageReportDir = "$testResultsRootDir/coverage-report" + New-Item -ItemType Directory -Force -Path $dir + New-Item -ItemType Directory -Force -Path "$dir/dotnet" + New-Item -ItemType Directory -Force -Path "$dir/work" + New-Item -ItemType Directory -Force -Path "$testResultsRootDir" + New-Item -ItemType Directory -Force -Path "$coverageReportDir" + Add-Content $env:GITHUB_ENV "`nDOTNET_INSTALL_DIR=$dir/dotnet" + Add-Content $env:GITHUB_ENV "`nWORKPATH=$dir/work" + Add-Content $env:GITHUB_ENV "`nTEST_RESULTS_ROOT_DIR=$testResultsRootDir" + Add-Content $env:GITHUB_ENV "`nCOVERAGE_REPORT_DIR=$coverageReportDir" + Add-Content $env:GITHUB_PATH "`n$($env:HOME)/.dotnet/tools" + + # Install the .NET SDK + - name: Setup .NET 8.0 + uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5 + with: + dotnet-version: 8.0.x + + - name: Download Test Binaries + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5 + with: + name: test-assemblies + path: ${{ env.WORKPATH }} + + - name: Install Coverage + shell: pwsh + run: dotnet tool install --global dotnet-coverage + + - name: Install ReportGenerator + shell: pwsh + run: dotnet tool install --global dotnet-reportgenerator-globaltool + + - name: Run Tests + shell: pwsh + run: | + # Imports + . (Join-Path $env:GITHUB_WORKSPACE 'eng' 'build' 'Markdown-Formatting.ps1') + + $includesCoverage = $false + $results = @() + + Get-ChildItem -Directory $env:WORKPATH | ForEach-Object { + $dllPath = Join-Path $_.FullName ($_.Name + ".dll") + $resultsDirectory = Join-Path $env:TEST_RESULTS_ROOT_DIR $_.Name + New-Item -ItemType Directory -Force -Path $resultsDirectory | Out-Null + + if (Test-Path $dllPath) { + Write-Host "Running tests in $dllPath..." + + # Build as array of arguments (excluding the "dotnet" prefix and -- arguments) + $testCmd = @( + "test", $dllPath, + "--framework", "${{ matrix.tfm }}", + "--logger:console;verbosity=normal", + "--logger:trx;LogFileName=TestResults.trx", + "--results-directory", $resultsDirectory, + "--blame-hang-timeout", "10m", + "--blame-hang-dump-type", "mini", + "--blame-crash" + ) + + if ("${{ matrix.arch }}" -eq 'arm64' -and ($env:RUNNER_OS -eq 'macOS' -or $env:RUNNER_OS -eq 'Linux')) { + # Run tests without coverage + dotnet @testCmd -- RunConfiguration.TargetPlatform=${{ matrix.arch }} + } else { + # Ensure a coverage folder per test project output + $coverageDir = Join-Path $resultsDirectory "coverage" + New-Item -ItemType Directory -Force -Path $coverageDir | Out-Null + $coverageFile = Join-Path $coverageDir "coverage.xml" + + # Collect coverage in Cobertura format; TRX is produced by the inner 'dotnet test' + dotnet-coverage collect "dotnet $($testCmd -join ' ') -- RunConfiguration.TargetPlatform=${{ matrix.arch }}" ` + --output-format cobertura ` + --output "$coverageFile" ` + --settings (Join-Path "$env:GITHUB_WORKSPACE" 'eng' 'build' 'coverage.runsettings') + + if (Test-Path $coverageFile) { $includesCoverage = $true } + } + + # TRX inspection / status computation + $trxFile = Join-Path $resultsDirectory "TestResults.trx" + if (Test-Path $trxFile) { + $parsed = & "$env:GITHUB_WORKSPACE/eng/build/Parse-Test-Results.ps1" -Path $trxFile + $parsed | Add-Member -NotePropertyName "SuiteName" -NotePropertyValue $_.Name + $results += $parsed + } + } + } + + # Write summary + Format-Test-Results $results | Add-Content $env:GITHUB_STEP_SUMMARY + + if ($includesCoverage) { + # Generate a per-job coverage summary + HTML reports from all coverage.xml files created above + $reportsGlob = Join-Path $env:TEST_RESULTS_ROOT_DIR "**/coverage/coverage.xml" + reportgenerator ` + "-reports:$reportsGlob" ` + "-targetdir:$env:COVERAGE_REPORT_DIR" ` + "-reporttypes:MarkdownSummaryGithub;HtmlInline_AzurePipelines;HtmlChart" ` + "-verbosity:Warning" + + # Append collapsible coverage section to the job summary + $md = Join-Path $env:COVERAGE_REPORT_DIR "SummaryGithub.md" + if (Test-Path $md) { + Add-Content $env:GITHUB_STEP_SUMMARY "`n
Coverage`n" + Get-Content $md | Add-Content $env:GITHUB_STEP_SUMMARY + Add-Content $env:GITHUB_STEP_SUMMARY "`n
`n" + } else { + Write-Host "Coverage summary not generated (no coverage.xml found)." + } + } + + # Report test failure + if ($results | Where-Object { $_.FailedCount -gt 0 -or $_.Crashed }) { + exit 1 + } + + - name: Upload Test Results + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: test-results-${{ matrix.os }}-${{ matrix.arch }} + path: ${{ env.TEST_RESULTS_ROOT_DIR }} + + coverage: + name: aggregate-coverage + needs: test + runs-on: ubuntu-latest + steps: + - name: Download all test result artifacts + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5 + with: + pattern: test-results-* + path: all-results + merge-multiple: true + + # Required for ReportGenerator + - name: Setup .NET SDK + uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5 + with: + dotnet-version: 8.0.x + + - name: Install ReportGenerator + shell: pwsh + run: | + dotnet tool install --global dotnet-reportgenerator-globaltool + Add-Content $env:GITHUB_PATH "`n$HOME/.dotnet/tools" + + - name: Build Aggregate Report + shell: pwsh + run: | + New-Item -ItemType Directory -Force -Path "coverage/aggregate" | Out-Null + reportgenerator ` + "-reports:all-results/**/coverage/coverage.xml" ` + "-targetdir:coverage/aggregate" ` + "-reporttypes:MarkdownSummaryGithub;HtmlInline_AzurePipelines;HtmlChart" ` + "-verbosity:Warning" + + $md = "coverage/aggregate/SummaryGithub.md" + if (Test-Path $md) { + Add-Content $env:GITHUB_STEP_SUMMARY "## Aggregate Coverage`n" + Get-Content $md | Add-Content $env:GITHUB_STEP_SUMMARY + } else { + Add-Content $env:GITHUB_STEP_SUMMARY "## Aggregate Coverage`n_No coverage files found._" + } + + - name: Upload Aggregate Coverage HTML + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: coverage-aggregate-html + path: coverage/aggregate + + release: + name: release + if: github.event_name == 'push' && github.ref_type == 'tag' + runs-on: ubuntu-latest + needs: + - build + - test + steps: + - name: Checkout Source + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + with: + fetch-depth: 0 + + - name: Get Tag Version + id: tagversion + shell: pwsh + run: | + $tag = '${{ github.ref_name }}' -replace '^v', '' + Add-Content $env:GITHUB_OUTPUT "version=$tag" + + # prerelease = true if contains a hyphen (semver prerelease part) + $prerelease = if ($tag -match '-') { 'true' } else { 'false' } + Add-Content $env:GITHUB_OUTPUT "`nprerelease=$prerelease" + + - name: Get Version from NBGV + id: nbgv + shell: pwsh + run: | + $nbgvOutput = & nbgv get-version + $nugetVersion = ($nbgvOutput | Where-Object { $_ -match '^NuGetPackageVersion:' }) -replace '^NuGetPackageVersion:\s+', '' + Add-Content $env:GITHUB_OUTPUT "version=$nugetVersion" + + - name: Download NuGet Packages + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5 + with: + name: nuget-packages + path: dist/nuget + - name: Verify Versions Match + shell: pwsh + run: | + $tagVersion = '${{ steps.tagversion.outputs.version }}' + $repoVersion = '${{ steps.nbgv.outputs.version }}' + + if ($tagVersion -ne $repoVersion) { + throw "Tag version $tagVersion does not match repository version $repoVersion. Make sure there are no typos in the Git tag name." + } + + # Also check that all .nupkg files contain the version + Get-ChildItem dist/nuget/*.nupkg | ForEach-Object { + if (-not $_.Name.Contains($tagVersion)) { + throw "Artifact version mismatch: $($_.Name) vs tag version $tagVersion" + } + } + - name: Create Draft GitHub Release + uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2 + with: + draft: true + prerelease: ${{ steps.tagversion.outputs.prerelease }} + files: dist/nuget/*.nupkg,dist/nuget/*.snupkg + generate_release_notes: true + token: ${{ secrets.GITHUB_TOKEN }} + + publish: + name: publish + if: github.event_name == 'release' && github.event.action == 'published' + runs-on: ubuntu-latest + steps: + - name: Download Release Assets + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const fs = require("fs"); + const path = require("path"); + + const release = context.payload.release; + if (!release) { + core.setFailed("No release payload found."); + return; + } + + const assets = release.assets || []; + const dir = "dist/nuget"; + fs.mkdirSync(dir, { recursive: true }); + + for (const asset of assets) { + if (!asset.name.endsWith(".nupkg") && !asset.name.endsWith(".snupkg")) { + continue; + } + core.info(`Downloading ${asset.name}...`); + const response = await github.request("GET /repos/{owner}/{repo}/releases/assets/{asset_id}", { + owner: context.repo.owner, + repo: context.repo.repo, + asset_id: asset.id, + headers: { Accept: "application/octet-stream" }, + }); + const filePath = path.join(dir, asset.name); + fs.writeFileSync(filePath, Buffer.from(response.data)); + core.info(`Saved to ${filePath}`); + } + - name: Push To NuGet + shell: pwsh + run: | + $files = Get-ChildItem "dist/nuget/*.nupkg", "dist/nuget/*.snupkg" -ErrorAction Ignore + foreach ($file in $files) { + dotnet nuget push $file.FullName --source $env:NUGET_SOURCE_URL --api-key $env:NUGET_API_KEY --skip-duplicate + } + env: + NUGET_SOURCE_URL: 'https://api.nuget.org/v3/index.json' + NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} diff --git a/.github/workflows/powershell-tests.yml b/.github/workflows/powershell-tests.yml new file mode 100644 index 0000000..2e53b36 --- /dev/null +++ b/.github/workflows/powershell-tests.yml @@ -0,0 +1,105 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: Test Powershell Scripts + +on: + workflow_dispatch: + push: + branches: + - main + - 'release/v*.*' # matches release/v1.2 + - 'release/v*.*.*' # matches release/v1.2.3 + - 'release-workflow*' # special branch names for testing release workflow (this file) from a PR + paths: + - '**/*.ps1' + - '**/*.psm1' + - '**/*.ps1xml' + - '**/*.pssc' + - '**/*.cdxml' + - '**/*.psrc' + - '**/*.psc1' + pull_request: + paths: + - '**/*.ps1' + - '**/*.psm1' + - '**/*.ps1xml' + - '**/*.pssc' + - '**/*.cdxml' + - '**/*.psrc' + - '**/*.psc1' + +# De-duplicate runs +concurrency: + group: test-powershell-${{ github.ref }} + cancel-in-progress: true + +jobs: + pester: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + + - name: Restore Powershell Dependencies + shell: pwsh + run: ./eng/Ensure-Powershell-Dependencies.ps1 + + - name: Run Pester Tests + shell: pwsh + run: | + # Imports + . (Join-Path $env:GITHUB_WORKSPACE 'eng' 'build' 'Markdown-Formatting.ps1') + + $ErrorActionPreference = 'Continue' + try { + $testResults = Invoke-Pester -Output Detailed -CI -PassThru + } catch { + Write-Warning "Invoke-Pester threw an exception: $_" + } + $ErrorActionPreference = 'Stop' + Set-StrictMode -Version Latest + + $failed = $false + $results = @() + + if ($testResults -and $testResults.Containers -and $testResults.Containers.Count -gt 0) { + foreach ($container in $testResults.Containers) { + $suiteName = $container.Item.Name + $passedCount = $container.PassedCount + $failedCount = $container.FailedCount + $skippedCount = $container.SkippedCount + + $results += [PSCustomObject]@{ + SuiteName = $suiteName + PassedCount = [int]$passedCount + FailedCount = [int]$failedCount + IgnoredCount = [int]$skippedCount + Crashed = [bool]$false + } + + if ($failedCount -gt 0) { + $failed = $true + } + } + } + + # Write summary + Format-Test-Results $results | Add-Content $env:GITHUB_STEP_SUMMARY + + if ($failed) { + exit 1 + } diff --git a/.github/workflows/renovate-dependencies.yml b/.github/workflows/renovate-dependencies.yml new file mode 100644 index 0000000..d0e00a4 --- /dev/null +++ b/.github/workflows/renovate-dependencies.yml @@ -0,0 +1,38 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: Renovate + +on: + workflow_dispatch: # allows manual runs + schedule: + - cron: "0 3 * * 1" # 3 AM Monday UTC (same as ASF-friendly schedule) + +jobs: + renovate: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + + - name: Run Renovate + uses: renovatebot/github-action@f8af9272cd94a4637c29f60dea8731afd3134473 # v43.0.12 + with: + token: ${{ secrets.RENOVATE_TOKEN }} + env: + RENOVATE_REPOSITORIES: ${{ github.repository }} + LOG_LEVEL: debug diff --git a/.gitignore b/.gitignore index 8dd4607..1f78d93 100644 --- a/.gitignore +++ b/.gitignore @@ -395,4 +395,25 @@ FodyWeavers.xsd *.msp # JetBrains Rider -*.sln.iml \ No newline at end of file +*.sln.iml + +########################################### +# Custom +########################################### + +# BeyondCompare .orig files (when doing conflict resolution) +*.orig + +# Build artifacts +_artifacts/ + +# Rider / ReSharper +.idea/ +*.sln.iml + +# Rider - allow shared inspection + code style profiles +!.idea/.idea.SPDX.CodeAnalysis/.idea/inspectionProfiles/ +!.idea/.idea.SPDX.CodeAnalysis/.idea/codeStyles/ + +# Custom Tools +.tools/* diff --git a/.rat-excludes b/.rat-excludes new file mode 100644 index 0000000..41fdbac --- /dev/null +++ b/.rat-excludes @@ -0,0 +1,17 @@ +# Note: these patterns are applied to single files or directories, not full paths +# coverage/* will ignore any coverage dir, but airflow/www/static/coverage/* will match nothing + +.git/* +.rat-excludes +.gitignore +.gitattributes + +# Exclude build assets +lib/* +obj/* +bin/* +_artifacts/* +_site/* + +# Exclude auto-generated designers +.*\.Designer\.cs diff --git a/DiagnosticCategoryAndIdRanges.txt b/DiagnosticCategoryAndIdRanges.txt new file mode 100644 index 0000000..588b28a --- /dev/null +++ b/DiagnosticCategoryAndIdRanges.txt @@ -0,0 +1,26 @@ +# This file contains the allowed analyzer rule "Category" and corresponding "Diagnostic ID range" +# FORMAT: +# 'Category': Comma separate list of 'StartId-EndId' or 'Id' or 'Prefix' + +# +# The ranges below represent the **currently used IDs** for the corresponding category. +# When implementing a new rule for the category, BEFORE submitting a PR: +# 1. Choose the rule ID immediately following the range end. +# 2. Update the range end to the chosen rule ID. +# 3. Commit and push the change to this file to `main`, with this file being the only file in the commit. +# 4. Your rule ID is now reserved and can be used in your PR. +# +# In the event of conflict in step 3, make sure you discard your changes, pull latest, and try again. +# DO NOT remove ID ranges already defined or merge this file in git. +# +Design: LuceneDev1000-LuceneDev1004 +Globalization: +Mobility: +Performance: +Security: +Usage: +Naming: +Interoperability: +Maintainability: +Reliability: +Documentation: \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..25245ae --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,93 @@ + + + + + $(MSBuildThisFileDirectory) + 12.0 + apache + lucenenet-codeanalysis-dev + + + + full + + + + false + + + + false + + + + false + + + + $(RepositoryRoot)eng/Lucene.Net.snk + 002400000480000094000000060200000024000052534131000400000100010075a07ce602f88ef263c7db8cb342c58ebd49ecdcc210fac874260b0213fb929ac3dcaf4f5b39744b800f99073eca72aebfac5f7284e1d5f2c82012a804a140f06d7d043d83e830cdb606a04da2ad5374cc92c0a49508437802fb4f8fb80a05e59f80afb99f4ccd0dfe44065743543c4b053b669509d29d332cd32a0cb1e97e84 + true + + + + false + true + + + + Lucene.Net + The Apache Software Foundation + $([System.DateTime]::UtcNow.Year.ToString()) + 2023 + $(BeginCopyrightYear) - $(CurrentYear) + $(CurrentYear) + Copyright © $(CopyrightYearRange) $(Company) + + + + true + true + + + + true + + + + + netstandard2.0 + + + $(MSBuildThisFileDirectory)..\_artifacts\noop\$(MSBuildProjectName)\bin\ + $(MSBuildThisFileDirectory)..\_artifacts\noop\$(MSBuildProjectName)\obj\ + $(BaseOutputPath) + $(BaseIntermediateOutputPath) + + + None + false + true + true + + + diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 0000000..4d2df08 --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,47 @@ + + + + + + + <_Parameter1>%(InternalsVisibleTo.Identity) + <_Parameter1 Condition=" '$(SignAssembly)' == 'true' And '$(PublicKey)' != '' ">%(InternalsVisibleTo.Identity), PublicKey=$(PublicKey) + + + + + + + true + $(TargetFramework) + + $(TargetFrameworks) + none + + + + + + + + + diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 0000000..84f2cd3 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,50 @@ + + + + true + true + + + + 4.14.0 + + + + + + + + + + + + + + + + + + + + + + diff --git a/LICENSE.txt b/LICENSE.txt index 93b51b1..a8a2434 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -187,7 +187,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2023 Shad Storhaug + Copyright 2023-2025 The Apache Software Foundation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Lucene.Net.CodeAnalysis.Dev.ChildProcessDbgSettings b/Lucene.Net.CodeAnalysis.Dev.ChildProcessDbgSettings new file mode 100644 index 0000000..42cff23 --- /dev/null +++ b/Lucene.Net.CodeAnalysis.Dev.ChildProcessDbgSettings @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Lucene.Net.CodeAnalysis.Dev.sln b/Lucene.Net.CodeAnalysis.Dev.sln index 44f51db..31531c0 100644 --- a/Lucene.Net.CodeAnalysis.Dev.sln +++ b/Lucene.Net.CodeAnalysis.Dev.sln @@ -1,21 +1,74 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. VisualStudioVersion = 17.5.33627.172 MinimumVisualStudioVersion = 10.0.40219.1 +Project("{13B669BE-BB05-4DDF-9536-439F39A36129}") = ".github", ".github\.github.folderproj", "{873A3BE7-3364-F423-9686-FFB58C0896C4}" +EndProject +Project("{13B669BE-BB05-4DDF-9536-439F39A36129}") = "docs", "docs\docs.folderproj", "{F1CC6D09-070B-440A-BDA2-C7A8037076ED}" +EndProject +Project("{13B669BE-BB05-4DDF-9536-439F39A36129}") = "eng", "eng\eng.folderproj", "{9A1C280A-5010-45E9-9EC8-B09F0F9517A2}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{1A48DD8E-1D71-43AE-B15D-977A87972623}" + ProjectSection(SolutionItems) = preProject + src\Directory.Build.props = src\Directory.Build.props + src\Directory.Build.targets = src\Directory.Build.targets + EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{8170F744-3AE0-41EE-8986-611BA2C20425}" + ProjectSection(SolutionItems) = preProject + tests\Directory.Build.props = tests\Directory.Build.props + tests\Directory.Build.targets = tests\Directory.Build.targets + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lucene.Net.CodeAnalysis.Dev.Vsix", "src\Lucene.Net.CodeAnalysis.Dev.Vsix\Lucene.Net.CodeAnalysis.Dev.Vsix.csproj", "{B9116527-2486-4A4C-90F8-378DF26E39AF}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lucene.Net.CodeAnalysis.Dev", "src\Lucene.Net.CodeAnalysis.Dev\Lucene.Net.CodeAnalysis.Dev.csproj", "{0611A6A8-372D-4E03-BAAF-731FF4844D6C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lucene.Net.CodeAnalysis.Dev.CodeFixes", "src\Lucene.Net.CodeAnalysis.Dev.CodeFixes\Lucene.Net.CodeAnalysis.Dev.CodeFixes.csproj", "{2A17E06E-D3A8-4E09-BB79-8FBE88B96074}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lucene.Net.CodeAnalysis.Dev.Tests", "tests\Lucene.Net.CodeAnalysis.Dev.Tests\Lucene.Net.CodeAnalysis.Dev.Tests.csproj", "{007DD065-3DF1-4AC1-9403-FE11E4654AF4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lucene.Net.CodeAnalysis.Dev.Sample", "src\Lucene.Net.CodeAnalysis.Dev.Sample\Lucene.Net.CodeAnalysis.Dev.Sample.csproj", "{62BE25C9-72F2-4348-96C4-7329252E9DE8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lucene.Net.CodeAnalysis.Dev.Package", "src\Lucene.Net.CodeAnalysis.Dev.Package\Lucene.Net.CodeAnalysis.Dev.Package.csproj", "{A476A043-926E-488B-A825-02EB0B410CFD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + .gitattributes = .gitattributes + .gitignore = .gitignore + DiagnosticCategoryAndIdRanges.txt = DiagnosticCategoryAndIdRanges.txt + Directory.Build.props = Directory.Build.props + Directory.Build.targets = Directory.Build.targets + Directory.Packages.props = Directory.Packages.props + global.json = global.json + LICENSE.txt = LICENSE.txt + Lucene.Net.CodeAnalysis.Dev.ChildProcessDbgSettings = Lucene.Net.CodeAnalysis.Dev.ChildProcessDbgSettings + NOTICE.txt = NOTICE.txt + README.md = README.md + renovate.json = renovate.json + version.json = version.json + EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lucene.Net.CodeAnalysis.Dev.Package", "src\Lucene.Net.CodeAnalysis.Dev.Package\Lucene.Net.CodeAnalysis.Dev.Package.csproj", "{CD8963C8-CC15-4C77-966A-42A861DFECDF}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lucene.Net.CodeAnalysis.Dev.CodeFixes", "src\Lucene.Net.CodeAnalysis.Dev.CodeFixes\Lucene.Net.CodeAnalysis.Dev.CodeFixes.csproj", "{94EE9776-30FC-4976-8C1D-AFE6A71BF8F5}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lucene.Net.CodeAnalysis.Dev.Vsix", "src\Lucene.Net.CodeAnalysis.Dev.Vsix\Lucene.Net.CodeAnalysis.Dev.Vsix.csproj", "{4B617551-92B7-48A4-9132-A260E4499533}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lucene.Net.CodeAnalysis.Dev.CodeFixes.Tests", "tests\Lucene.Net.CodeAnalysis.Dev.CodeFixes.Tests\Lucene.Net.CodeAnalysis.Dev.CodeFixes.Tests.csproj", "{2ADD8F4F-8360-4994-9451-B6741842AC58}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lucene.Net.Tests.CodeAnalysis.Dev", "tests\Lucene.Net.Tests.CodeAnalysis.Dev\Lucene.Net.Tests.CodeAnalysis.Dev.csproj", "{007DD065-3DF1-4AC1-9403-FE11E4654AF4}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lucene.Net.CodeAnalysis.Dev.TestUtilities", "tests\Lucene.Net.CodeAnalysis.Dev.TestUtilities\Lucene.Net.CodeAnalysis.Dev.TestUtilities.csproj", "{D7D95A33-0FB1-4F2A-A5CD-27719995026C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -23,36 +76,63 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {873A3BE7-3364-F423-9686-FFB58C0896C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {873A3BE7-3364-F423-9686-FFB58C0896C4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {873A3BE7-3364-F423-9686-FFB58C0896C4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {873A3BE7-3364-F423-9686-FFB58C0896C4}.Release|Any CPU.Build.0 = Release|Any CPU + {F1CC6D09-070B-440A-BDA2-C7A8037076ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1CC6D09-070B-440A-BDA2-C7A8037076ED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1CC6D09-070B-440A-BDA2-C7A8037076ED}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1CC6D09-070B-440A-BDA2-C7A8037076ED}.Release|Any CPU.Build.0 = Release|Any CPU + {9A1C280A-5010-45E9-9EC8-B09F0F9517A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9A1C280A-5010-45E9-9EC8-B09F0F9517A2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9A1C280A-5010-45E9-9EC8-B09F0F9517A2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9A1C280A-5010-45E9-9EC8-B09F0F9517A2}.Release|Any CPU.Build.0 = Release|Any CPU + {B9116527-2486-4A4C-90F8-378DF26E39AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B9116527-2486-4A4C-90F8-378DF26E39AF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B9116527-2486-4A4C-90F8-378DF26E39AF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B9116527-2486-4A4C-90F8-378DF26E39AF}.Release|Any CPU.Build.0 = Release|Any CPU {0611A6A8-372D-4E03-BAAF-731FF4844D6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0611A6A8-372D-4E03-BAAF-731FF4844D6C}.Debug|Any CPU.Build.0 = Debug|Any CPU {0611A6A8-372D-4E03-BAAF-731FF4844D6C}.Release|Any CPU.ActiveCfg = Release|Any CPU {0611A6A8-372D-4E03-BAAF-731FF4844D6C}.Release|Any CPU.Build.0 = Release|Any CPU - {2A17E06E-D3A8-4E09-BB79-8FBE88B96074}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2A17E06E-D3A8-4E09-BB79-8FBE88B96074}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2A17E06E-D3A8-4E09-BB79-8FBE88B96074}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2A17E06E-D3A8-4E09-BB79-8FBE88B96074}.Release|Any CPU.Build.0 = Release|Any CPU - {CD8963C8-CC15-4C77-966A-42A861DFECDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CD8963C8-CC15-4C77-966A-42A861DFECDF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CD8963C8-CC15-4C77-966A-42A861DFECDF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CD8963C8-CC15-4C77-966A-42A861DFECDF}.Release|Any CPU.Build.0 = Release|Any CPU - {4B617551-92B7-48A4-9132-A260E4499533}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4B617551-92B7-48A4-9132-A260E4499533}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4B617551-92B7-48A4-9132-A260E4499533}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4B617551-92B7-48A4-9132-A260E4499533}.Release|Any CPU.Build.0 = Release|Any CPU {007DD065-3DF1-4AC1-9403-FE11E4654AF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {007DD065-3DF1-4AC1-9403-FE11E4654AF4}.Debug|Any CPU.Build.0 = Debug|Any CPU {007DD065-3DF1-4AC1-9403-FE11E4654AF4}.Release|Any CPU.ActiveCfg = Release|Any CPU {007DD065-3DF1-4AC1-9403-FE11E4654AF4}.Release|Any CPU.Build.0 = Release|Any CPU + {62BE25C9-72F2-4348-96C4-7329252E9DE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {62BE25C9-72F2-4348-96C4-7329252E9DE8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {62BE25C9-72F2-4348-96C4-7329252E9DE8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {62BE25C9-72F2-4348-96C4-7329252E9DE8}.Release|Any CPU.Build.0 = Release|Any CPU + {A476A043-926E-488B-A825-02EB0B410CFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A476A043-926E-488B-A825-02EB0B410CFD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A476A043-926E-488B-A825-02EB0B410CFD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A476A043-926E-488B-A825-02EB0B410CFD}.Release|Any CPU.Build.0 = Release|Any CPU + {94EE9776-30FC-4976-8C1D-AFE6A71BF8F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {94EE9776-30FC-4976-8C1D-AFE6A71BF8F5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {94EE9776-30FC-4976-8C1D-AFE6A71BF8F5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {94EE9776-30FC-4976-8C1D-AFE6A71BF8F5}.Release|Any CPU.Build.0 = Release|Any CPU + {2ADD8F4F-8360-4994-9451-B6741842AC58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2ADD8F4F-8360-4994-9451-B6741842AC58}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2ADD8F4F-8360-4994-9451-B6741842AC58}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2ADD8F4F-8360-4994-9451-B6741842AC58}.Release|Any CPU.Build.0 = Release|Any CPU + {D7D95A33-0FB1-4F2A-A5CD-27719995026C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D7D95A33-0FB1-4F2A-A5CD-27719995026C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D7D95A33-0FB1-4F2A-A5CD-27719995026C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D7D95A33-0FB1-4F2A-A5CD-27719995026C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution + {B9116527-2486-4A4C-90F8-378DF26E39AF} = {1A48DD8E-1D71-43AE-B15D-977A87972623} {0611A6A8-372D-4E03-BAAF-731FF4844D6C} = {1A48DD8E-1D71-43AE-B15D-977A87972623} - {2A17E06E-D3A8-4E09-BB79-8FBE88B96074} = {1A48DD8E-1D71-43AE-B15D-977A87972623} - {CD8963C8-CC15-4C77-966A-42A861DFECDF} = {1A48DD8E-1D71-43AE-B15D-977A87972623} - {4B617551-92B7-48A4-9132-A260E4499533} = {1A48DD8E-1D71-43AE-B15D-977A87972623} {007DD065-3DF1-4AC1-9403-FE11E4654AF4} = {8170F744-3AE0-41EE-8986-611BA2C20425} + {62BE25C9-72F2-4348-96C4-7329252E9DE8} = {1A48DD8E-1D71-43AE-B15D-977A87972623} + {A476A043-926E-488B-A825-02EB0B410CFD} = {1A48DD8E-1D71-43AE-B15D-977A87972623} + {94EE9776-30FC-4976-8C1D-AFE6A71BF8F5} = {1A48DD8E-1D71-43AE-B15D-977A87972623} + {2ADD8F4F-8360-4994-9451-B6741842AC58} = {8170F744-3AE0-41EE-8986-611BA2C20425} + {D7D95A33-0FB1-4F2A-A5CD-27719995026C} = {8170F744-3AE0-41EE-8986-611BA2C20425} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B261893F-67D2-4098-B66F-9191951DB5BB} diff --git a/NOTICE.txt b/NOTICE.txt new file mode 100644 index 0000000..3a76bc6 --- /dev/null +++ b/NOTICE.txt @@ -0,0 +1,5 @@ +Apache Lucene.Net +Copyright 2023-2025 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). diff --git a/NuGet.config b/NuGet.config deleted file mode 100644 index 745e6c9..0000000 --- a/NuGet.config +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/README.md b/README.md new file mode 100644 index 0000000..18002cd --- /dev/null +++ b/README.md @@ -0,0 +1,79 @@ + + +# Apache Lucene.NET Dev Analyzers + +This repo contains custom [Roslyn analyzers](https://learn.microsoft.com/en-us/visualstudio/code-quality/roslyn-analyzers-overview?view=vs-2022) that are used by the [Apache Lucene.NET](https://lucenenet.apache.org/) project to enforce code quality and consistency, as well as provide automated code fixes. + +These analyzers are intended for use by contributors to the Lucene.NET project only. +They are not intended for public use outside of this project. +Therefore, any releases or NuGet packages produced from this repository are not official Apache Lucene.NET project artifacts, and are not subject to the same quality control or testing as the official Lucene.NET releases. +They are also not subject to the release policy or voting process, as these are not intended +to be used "beyond the group that owns it." + +## Building + +To build the analyzers, you will need to have the [.NET 8 SDK](https://dotnet.microsoft.com/download) installed. + +To build from the repo root, run the following command: + +```bash +dotnet build +``` + +To run the tests, you can use the following command: + +```bash +dotnet test +``` + +## IDE Support and Debugging + +These analyzers have been tested with Visual Studio 2022 and JetBrains Rider. They should work with any IDE that supports Roslyn analyzers, but your mileage may vary. Importantly, they also work with MSBuild, so they can be used in our CI pipelines, or to help validate your changes when you build before submitting a pull request. + +A `Lucene.Net.CodeAnalysis.Dev.Sample` project has been provided to demonstrate and debug the analyzers and code fixes in the IDE during development of them. After building, you should notice the analyzers producing the expected warnings in the Sample project. You can also debug the analyzers by setting a breakpoint in your analyzer and launching the `DebugRoslynAnalyzers` target of the `Lucene.Net.CodeAnalysis.Dev` project. You can also debug them by debugging the unit tests. + +## Contributing + +Please read and follow the [Apache Lucene.NET Contributor's Guide](https://github.com/apache/lucenenet/blob/master/CONTRIBUTING.md) first before proceeding further. + +### Reserving a Diagnostic ID + +Before creating any analyzers, you'll need a reserved diagnostic ID for your analyzer(s). +To avoid multiple contributors attempting to use the same ID at the same time, we have created a simple process to follow. +It is important that you follow this process to avoid rework of your PR. + +1. Make sure there is an issue on the main [Apache Lucene.NET repo](https://github.com/apache/lucenenet/issues) for the analyzer(s) needed, that has been approved by the Lucene.NET team as indicated by having the `approved-rule` label. +2. Reserve your diagnostic ID(s) _before_ implementing the analyzer(s): + - If you are a Lucene.NET committer, you can reserve one yourself. Modify the [DiagnosticCategoryAndIdRanges.txt](DiagnosticCategoryAndIdRanges.txt) file (following the instructions in that file) to reserve your ID(s). Commit and push the change to this file directly to the `main` branch, as the only file in the commit. DO NOT include any other code or changes in this commit. In the event of a conflict, do not merge this file; discard your changes, pull latest, and try again. Include the issue number in your commit message. + - If you are not a Lucene.NET committer, request in the discussion for the GitHub issue that a committer do the steps above for you for your desired number of diagnostic IDs. Please make sure to mention which category the ID(s) should belong to. +3. Once you have the reserved ID(s), you can proceed with implementing your analyzer and submitting a pull request. Make sure to include your analyzer in the [AnalyzerReleases.Unshipped.md](src/Lucene.Net.CodeAnalysis.Dev/AnalyzerReleases.Unshipped.md) file. + +### Requirements + +Before submitting a pull request to this repo, ensure that each analyzer you're adding: +1. Has a reserved ID (see above) +2. Has an entry in the [AnalyzerReleases.Unshipped.md](src/Lucene.Net.CodeAnalysis.Dev/AnalyzerReleases.Unshipped.md) file, reflowing the table if needed +3. Matches existing analyzer naming conventions and code styles +4. Has a title, description, and message format resource in the [Resources.resx](src/Lucene.Net.CodeAnalysis.Dev/Resources.resx) file (currently English only) +5. Has a working, sample violation in the `Lucene.Net.CodeAnalysis.Dev.Sample` project +6. Has good unit test coverage in the `Lucene.Net.CodeAnalysis.Dev.Tests` project, using existing styles and practices per the other unit tests there + diff --git a/branding/lucene-net-icon-128x128.png b/branding/lucene-net-icon-128x128.png new file mode 100644 index 0000000..85cbdee Binary files /dev/null and b/branding/lucene-net-icon-128x128.png differ diff --git a/docs/building-and-testing.md b/docs/building-and-testing.md new file mode 100644 index 0000000..a984a7a --- /dev/null +++ b/docs/building-and-testing.md @@ -0,0 +1,80 @@ + + +# Building and Testing + +## Command Line + +### Prerequisites + +- [.NET 8 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) + +### Building + +> **NOTE:** If the project is open in Visual Studio, its background restore may interfere with these commands. It is recommended to close all instances of Visual Studio that have this project open before executing. + +To build the source, clone or download and unzip the repository. From the repository or distribution root, execute the [**dotnet build**](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-build) command from a command prompt and include the desired options. + +#### Example +```console +dotnet build -c Release +``` + +> [!NOTE] +> NuGet packages are output by the build to the `/_artifacts/NuGetPackages/` directory. + +You can setup Visual Studio to read the NuGet packages like any NuGet feed by following these steps: + +1. In Visual Studio, right-click the solution in Solution Explorer, and choose "Manage NuGet Packages for Solution" +2. Click the gear icon next to the Package sources drop-down. +3. Click the `+` icon (for add) +4. Give the source a name such as `Lucene.Net.CodeAnalysis.Dev Local Packages` +5. Click the `...` button next to the Source field, and choose the `/_artifacts/NuGetPackages` folder on your local system. +6. Click OK + +Then all you need to do is choose the `Lucene.Net.CodeAnalysis.Dev Local Packages` feed from the dropdown (in the NuGet Package Manager) and you can search for, install, and update the NuGet packages just as you can with any Internet-based feed. + +### Testing + +Similarly to the build command, run [**dotnet test**](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-build) with the desired options. + +#### Example +```console +dotnet test -c Release --logger:"console;verbosity=normal" +``` + +## Visual Studio + +### Prerequisites + +1. Visual Studio 2022 or higher +2. [.NET 8.0 SDK](https://dotnet.microsoft.com/download/visual-studio-sdks) or higher + +### Execution + +1. Open `Lucene.Net.CodeAnalysis.Dev.sln` in Visual Studio. +2. Build a project or the entire solution, and wait for Visual Studio to discover the tests. +3. Run or debug the tests in Test Explorer, optionally using the desired filters. + +> [!TIP] +> When running tests in Visual Studio, [set the default processor architecture to x86, x64, or ARM64](https://stackoverflow.com/a/45946727) as applicable to your operating system. +> +> ![Test Explorer Architecture Settings](images/vs-test-architecture.png) diff --git a/docs/docs.folderproj b/docs/docs.folderproj new file mode 100644 index 0000000..8933ebd --- /dev/null +++ b/docs/docs.folderproj @@ -0,0 +1,25 @@ + + + + + + diff --git a/docs/images/release-build-outcomes.md b/docs/images/release-build-outcomes.md new file mode 100644 index 0000000..9fcc163 --- /dev/null +++ b/docs/images/release-build-outcomes.md @@ -0,0 +1,16 @@ +This markup can be edited and converted to .svg or .png here: +https://www.mermaidchart.com/app/projects/95759d78-db93-499c-ad66-0e3f698ba88c/diagrams/31dbd6bc-7ec8-4583-a456-55e3fe3f6cfc/version/v0.1/edit + +```mermaid +%%{ init: { "themeVariables": { "fontSize": "24px" } } }%% +flowchart TD + A["Tag + Push release branch"] --> B{"Draft Release generated?"} + B -- Yes --> C["Review release notes"] + C --> D["Check release artifacts"] + D --> E["Publish Release to NuGet.org"] + B -- No --> F["Check GitHub Actions logs"] + F --> G["Fix problems"] + G --> H["Delete failed tag"] + H --> I["Reset AnalyzerReleases.Shipped.md
header to {{vnext}} if needed"] + I --> A +``` diff --git a/docs/images/release-build-outcomes.svg b/docs/images/release-build-outcomes.svg new file mode 100644 index 0000000..c9c6b07 --- /dev/null +++ b/docs/images/release-build-outcomes.svg @@ -0,0 +1,102 @@ +

Yes

No

Tag + Push release branch

Draft Release generated?

Review release notes

Check release artifacts

Publish Release to NuGet.org

Check GitHub Actions logs

Fix problems

Delete failed tag

Reset AnalyzerReleases.Shipped.md
header to {{vnext}} if needed

\ No newline at end of file diff --git a/docs/images/release-workflow.md b/docs/images/release-workflow.md new file mode 100644 index 0000000..4f1728d --- /dev/null +++ b/docs/images/release-workflow.md @@ -0,0 +1,16 @@ +This markup can be edited and converted to .svg or .png here: +https://www.mermaidchart.com/app/projects/95759d78-db93-499c-ad66-0e3f698ba88c/diagrams/35faa26e-5ccf-4433-962e-32f20496471c/version/v0.1/edit + +```mermaid +flowchart LR + main[Main Branch] + release[Release Branch] + tag[Git Tag] + draft[Draft Release] + publish[Publish Release] + + main -->|Prepare Release| release + release -->|Tag Version| tag + tag --> draft + draft -->|Manual Review| publish +``` diff --git a/docs/images/release-workflow.svg b/docs/images/release-workflow.svg new file mode 100644 index 0000000..113591c --- /dev/null +++ b/docs/images/release-workflow.svg @@ -0,0 +1,122 @@ + + + +

Prepare Release

Tag Version

Manual Review

Main Branch

Release Branch

Git Tag

Draft Release

Publish Release

diff --git a/docs/images/vs-child-process-debugging-settings.png b/docs/images/vs-child-process-debugging-settings.png new file mode 100644 index 0000000..75e3cd8 Binary files /dev/null and b/docs/images/vs-child-process-debugging-settings.png differ diff --git a/docs/images/vs-native-debugging-setting.png b/docs/images/vs-native-debugging-setting.png new file mode 100644 index 0000000..895789e Binary files /dev/null and b/docs/images/vs-native-debugging-setting.png differ diff --git a/docs/images/vs-test-architecture.png b/docs/images/vs-test-architecture.png new file mode 100644 index 0000000..29857ac Binary files /dev/null and b/docs/images/vs-test-architecture.png differ diff --git a/docs/make-release.md b/docs/make-release.md new file mode 100644 index 0000000..c9fdef4 --- /dev/null +++ b/docs/make-release.md @@ -0,0 +1,434 @@ + + +# Making a Release + +> [!NOTE] +> All commands should be executed from the root of the repository unless otherwise stated. + +## Prerequisites + +- [PowerShell](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell) 6.0 or higher (see [this question](http://stackoverflow.com/questions/1825585/determine-installed-powershell-version) to check your PowerShell version) +- [.NET 8 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) +- [nbgv tool](https://www.nuget.org/packages/nbgv/) (the version must match the one defined in [Directory.Packages.props](../Directory.Packages.props)) +- [Java 8](https://adoptium.net/temurin/releases) or higher (either a JRE or JDK) +- Bash (Installed automatically with [Git for Windows](https://gitforwindows.org/) on Windows). + +### Installing the NBGV Tool + +Perform a one-time install of the nbgv tool using the following dotnet CLI command: + +> [!NOTE] +> The version should match the one used in [Directory.Packages.props](../Directory.Packages.props). + +```console +dotnet tool install -g nbgv --version +``` + +### Configure the Git Commit Hooks + +To synchronize the `AnalyzerReleases.Shipped.md` release version with the latest commit, there is a Git commit hook that ensures that the version in the HEAD commit is the same version that is committed to the file. + +Check whether the Git `core.hooksPath` is correctly set: + +```console +git config core.hooksPath +``` + +If the command outputs a path, confirm that the path is `./eng/git-hooks`. In all other cases, run the following command to set it appropriately. + +```console +git config core.hooksPath ./eng/git-hooks +``` + +Repeat the first command to confirm that it is set. + +--------------------------------------------- + +## Prior to Release + +This project uses Nerdbank.GitVersioning to assist with creating version numbers based on the current branch and commit. This tool handles making pre-release and production releases on release branches. + +### Release Workflow Overview + +![Release Workflow](images/release-workflow.svg) + +### Prepare the Main Branch + +1. Ensure all of the features that will be included have been merged to the `main` branch. +2. Check whether the `AnalyzerReleases.Unshipped.md` and `AnalyzerReleases.Shipped.md` are set up consistently and align with the features that have been merged since the prior release. Do not yet move any rules from `AnalyzerReleases.Unshipped.md` to `AnalyzerReleases.Shipped.md`. That task will be performed in a later step. +3. Check whether the `README`, `LICENSE`, `NOTICE` and other documentation files are up to date. + +If any changes are required, it is recommended to use feature branch(es) and pull request(s) to update the `main` branch as appropriate before creating a release branch. + +### Decide on a Release Version + +The version that will be released next is controlled by the `version.json` file. We must choose the release version and commit it to the `main` branch prior to creating a release branch. + +> [!NOTE] +> If you are not familiar with these terms, these are covered in the [Semantic Versioning 2.0](https://semver.org/spec/v2.0.0.html) document. + +For the purposes of this project: + +- **Major (Advanced)** - Released only when a new port of Lucene.NET is started (primarily to show a relationship between Lucene.NET and these analyzers) +- **Minor** - A typical release with one or more new features +- **Patch** - A release that only contains patches to existing features and/or updates to documentation +- **Prerelease** - A release that requires stabilization or is a one-off release for a specific purpose + +> [!NOTE] +> This project doesn't have any public API that users consume, so the type of release is strictly informational in nature, not functional. + +Now is the time to decide which of these strategies to use for the current version. For the next version (a future release version), we should always assume a patch. This is primarily so we never have to downgrade a version even if a patch is rarely done in practice. + +With that in mind, open `version.json` and look at the "version" property, which will determine next version that will be released. + +#### Example Version + +```json + "version": "2.0.0-alpha.{height}" +``` + +The above example shows that the next version that will be released from a release branch is 2.0.0 or 2.0.0-beta.x (where x is an auto-incrementing number). The actual version in the file (alpha) will be used only if the `main` branch is released directly (something that is rare and not covered here). + +If we are releasing new features and want the next Minor version (2.1.0), we need to update the `version.json` file to reflect that version. + +```json + "version": "2.1.0-alpha.{height}" +``` + +Or, if the next version will be a patch, then leave the file unchanged. Commit any changes to the `main` branch and push them upstream before proceeding. + +Prereleases should rarely need to change the `version.json` file and will later choose the [Requires Stabilization](#requires-stabilization) option when creating a release branch. + +> [!IMPORTANT] +> Release version numbers must always use all 3 version components when specified in `version.json`. + +## Create a Release Branch + +There are 2 supported scenarios for the release workflow: + +1. [Ready to Release](#ready-to-release) - No additional stabilization is required +2. [Requires Stabilization](#requires-stabilization) - A beta will be released, which will be marked as a pre-release to consumers + +> [!NOTE] +> In both cases, `main` is advanced to the specified `--nextVersion`. This number should always be a **patch** bump and it should always use all 3 version components (major.minor.patch). +> +> The release branch name is always based on the version being released (e.g., `release/v2.0.0`). + +### Ready to Release + +When the changes in the main branch are ready to release, create a release branch using the following nbgv tool command as specified in the [documentation](https://dotnet.github.io/Nerdbank.GitVersioning/docs/nbgv-cli.html#preparing-a-release). + +For example, assume the `version.json` file on the main branch is currently set up as `2.0.0-alpha.{height}`. We want to go from this version to a release of `2.0.0` and set the next version on the main branch as `2.0.1-alpha.{height}`. + +```console +nbgv prepare-release --nextVersion 2.0.1 +``` + +The command should respond with: + +```console +release/v2.0.0 branch now tracks v2.0.0 stabilization and release. +main branch now tracks v2.0.1-alpha.{height} development. +``` + +The tool created a release branch named `release/v2.0.0`. Every build from this branch will be versioned 2.0.0, regardless of how many commits are added. + +### Requires Stabilization + +When creating a release that may require a few iterations to become stable, it is better to create a beta branch (more about that decision can be found [here](https://dotnet.github.io/Nerdbank.GitVersioning/docs/nbgv-cli.html#preparing-a-release)). Starting from the same point as the [Ready to Release](#ready-to-release) scenario, run the following command. + +```console +nbgv prepare-release beta --nextVersion 2.0.1 +``` + +The command should respond with: + +```console +release/v2.0.0 branch now tracks v2.0.0-beta.{height} stabilization and release. +main branch now tracks v2.0.1-alpha.{height} development. +``` + +The tool created a release branch named `release/v2.0.0`. Every commit to this branch will be given a unique pre-release version starting with 2.0.0-beta and ending in a dot followed by one or more digits (i.e. `2.0.0-beta.123`). + +### Checkout the Release Branch + +After the release branch is created, the rest of the commits will be added to the release branch, so use the git checkout command to switch to that branch. + +```console +git checkout +``` + +--------------------------------------------- + +## Run the Apache Release Audit Tool + +> [!IMPORTANT] +> This command depends on Powershell and Java. + +The Release Audit Tool will ensure that all source code files and most other non-generated text files contain a license header. + +```console +pwsh ./rat.ps1 +``` + +The tool will apply the updates directly to the local working directory. Review and commit the changes to your local Git clone, adding exclusions to `.rat-excludes` and re-running as necessary. + +- Exclude files that already include license headers +- Exclude files that are automatically generated +- Exclude files that cannot contain license headers (such as test data) + +> [!NOTE] +> These extra commits will automatically bump the version number from what was specified when [Creating a Release Branch](creating-a-release-branch). It is normal and expected that we may have extra gaps between release version numbers. + + +## Updating the AnalyzerReleases Files + +Roslyn analyzers use two release tracking files to manage analyzer rule metadata: + +- **`AnalyzerReleases.Unshipped.md`** + Tracks analyzer rules that have been added or modified since the last release but are not yet published in a shipped package. + +- **`AnalyzerReleases.Shipped.md`** + Tracks analyzer rules that have been released in one or more shipped packages. This is the authoritative record of rules shipped at specific versions. + +Before tagging the release, you must ensure that these files are up to date. This ensures that the release metadata exactly matches the rules shipped in the NuGet package. + +> [!NOTE] +> If the release doesn't contain new or changed analyzer rules, this step can be skipped. For example, if the release only contains new code fixes and/or backward compatible patches to existing analyzers. + +### Release Version Token + +Since Nerdbank.GitVersioning calculates the release version, the `AnalyzerReleases.Shipped.md` file is expected to include a version token when it is committed. A version token must be included in the header of the new section being added to `AnalyzerReleases.Shipped.md`. + +#### Release Version Token Example + +```markdown +## Release {{vnext}} +``` + +### Standard Workflow + +> [!IMPORTANT] +> This change is expected to be the **final** commit prior to release. If there are any other changes you anticipate that need to be included in the release, they should be committed to the release branch prior to this step. + +> [!IMPORTANT] +> This step depends on the NBGV tool, Bash, and the setup of the Git commit hook as described in [Prerequisites](#prerequisites). + +1. **Locate pending unshipped rules** + Open `AnalyzerReleases.Unshipped.md`. This contains all rules added or modified since the last release. + +2. **Move unshipped rules into `AnalyzerReleases.Shipped.md`** + - Create a new section in `AnalyzerReleases.Shipped.md` with a heading for the release version, containing the version token. + - Copy the rules listed under `AnalyzerReleases.Unshipped.md` into this section. + - Keep the table formatting consistent with previous releases. + +3. **Clear `AnalyzerReleases.Unshipped.md`** + After the rules are copied over, `AnalyzerReleases.Unshipped.md` should either be empty or contain only rules that are not part of this release. + +4. **Commit the changes** + Commit the modifications before tagging the release. + +### Example: First and Second Releases with Version Token + +`AnalyzerReleases.Shipped.md` evolves by appending each release as a new section. Each release is marked with a `## Release ` header. + +```markdown +## Release 2.0.0-alpha.1 + +### New Rules + + Rule ID | Category | Severity | Notes +---------------|----------|----------|----------------------------------------- + LuceneDev1000 | Design | Warning | Floating point types should not be compared for exact equality + LuceneDev1001 | Design | Warning | Floating point types should be formatted with J2N methods + +## Release {{vnext}} + +### New Rules + + Rule ID | Category | Severity | Notes +---------------|----------|----------|----------------------------------------- + LuceneDev1002 | Design | Warning | Floating point type arithmetic needs to be checked + +### Removed Rules + + Rule ID | Notes +---------------|------------------------------------------------- + LuceneDev1001 | Replaced with LuceneDev1002 (better precision) +``` + +--------------------------------------------- + +## Creating a Release Build + +The release process is mostly automated. However, a manual review is required on the GitHub releases page. This allows you to: + +1. Manually review and edit the release notes +2. Re-generate the release notes after editing PR tags and titles +3. Manually check the release packages +4. Abort the release to try again +5. Publish the release to deploy the packages to NuGet.org + +

+ Release Build Outcomes +

+ +### Create a Draft Release + +Tagging the commit and pushing it to the GitHub repository will start the automated draft release. The progress of the release can be viewed in the [GitHub Actions UI](https://github.com/apache/lucenenet-codeanalysis-dev/actions). Select the run corresponding to the version tag that is pushed upstream to view the progress. + +#### Tag the HEAD Commit + +Run the following command to tag the HEAD commit of the release branch. + +```console +nbgv tag +``` + +> [!NOTE] +> The release build workflow always builds from the HEAD commit of the release branch. + +#### Push the Release Branch to the Upstream Repository + +The final step to begin the release build is to push the tag and any new commits to the upstream repository. + +```console +git push --follow-tags +``` + +> [!NOTE] +> If there are any local commits that have not yet been pushed, the above command will include them in the release. + +The push will start the automated draft release which will take a few minutes. When completed, there will be a new draft release in the [GitHub Releases](https://github.com/apache/lucenenet-codeanalysis-dev/releases) corresponding to the version you tagged. + +> [!NOTE] +> If the release doesn't appear, check the [GitHub Actions UI](https://github.com/apache/lucenenet-codeanalysis-dev/actions). Select the run corresponding to the version tag that is pushed upstream to view the progress. + +There are 2 possible outcomes for the release workflow: + +1. [Successful Draft Release](#successful-draft-release) - Proceed normally +2. [Failed Draft Release](#failed-draft-release) - Fix the problem that caused the release failure and reset the release branch for release + +--------------------------------------------- + +### Successful Draft Release + +#### Release Notes + +Review the draft release notes and edit or regenerate them if necessary. Release notes are generated based on PR titles and categorized by their labels. If something is amiss, they can be corrected by editing the PR titles and labels, deleting the previously generated release notes, and clicking the Generate Release Notes button. + +##### Labels that Apply to the Release Notes + +The following labels are recognized by the release notes generator. + +| GitHub Label | Action | +|--------------------------------|----------------------------------------------------------| +| notes:ignore | Removes the PR from the release notes | +| notes:breaking-change | Categorizes the PR under "Breaking Changes" | +| notes:new-feature | Categorizes the PR under "New Features" | +| notes:bug-fix | Categorizes the PR under "Bug Fixes" | +| notes:performance-improvement | Categorizes the PR under "Performance Improvements" | +| notes:improvement | Categorizes the PR under "Improvements" | +| notes:website-or-documentation | Categorizes the PR under "Website and API Documentation" | +| \ | Categorizes the PR under "Other Changes" | + +> [!NOTE] +> Using multiple labels from the above list is not supported and the first category in the above list will be used if more than one is applied to a GitHub pull request. + +#### Release Artifacts + +The release will also attach the NuGet packages that will be released to NuGet. Download the packages and run some basic checks: + +1. Put the `.nupkg` files into a local directory, and add a reference to the directory from Visual Studio. See [this answer](https://stackoverflow.com/a/10240180) for the steps. Verify that the NuGet packages can be referenced by a new project and that the project compiles. +2. Check the version information in [JetBrains dotPeek](https://www.jetbrains.com/decompiler/) to ensure the assembly version, file version, and informational version are consistent with what was specified in `version.json`. +3. Open the `.nupkg` files in [NuGet Package Explorer](https://www.microsoft.com/en-us/p/nuget-package-explorer/9wzdncrdmdm3#activetab=pivot:overviewtab) and check that files in the packages are present and that the XML config is up to date. + +#### Publish the Release + +Once everything is in order, the release can be published, which will deploy the packages to NuGet.org automatically. + +> [!NOTE] +> While the deployment will probably succeed, note that there is currently no automation if it fails to deploy on the first try. The GitHub API key must be regenerated once per year. If you are uncertain that it is still valid, check the expiry date in the NuGet.org portal now and regenerate, if needed. Update the `NUGET_API_KEY` in [GitHub Secrets](https://github.com/apache/lucenenet-codeanalysis-dev/settings/secrets/actions) with the new key. + +At the bottom of the draft release page, click on **Publish release**. + +--------------------------------------------- + +### Failed Draft Release + +If the build failed in any way, the release can be restarted by deleting the tag and trying again. First check to see the reason why the build failed in the [GitHub Actions UI](https://github.com/apache/lucenenet-codeanalysis-dev/actions) and correct any problems that were reported. + +#### Restarting the Draft Release + +##### Delete the Failed Tag + +Since the tag did not result in a release, it is important to delete it to avoid a confusing release history. + +```console +git tag -d v +git push --delete v +``` + +##### Resetting the Version in `AnalyzerReleases.Shipped.md` + +If you previously added a new section to `AnalyzerReleases.Shipped.md`, it may contain a version number that no longer corresponds to the release. Change the release header to include the replacement token, once again. + +```markdown +## Release {{vnext}} +``` + +Then commit the change to the release branch. + +Next, follow the same procedure starting at [Tag the HEAD Commit](#tag-the-head-commit) to restart the draft release. + +--------------------------------------------- + +## Post Release Steps + +### Merge the Release Branch + +Finally, merge the release branch back into the main branch and push the changes to the upstream repository. + +> [!IMPORTANT] +> Release branches start with `release/v`. + +```console +git checkout main +git merge +git push main +``` + +### Delete the Release Branch + +From this point, the release will be tracked historically using the Git tag, so there is no reason to keep the release branch once it has been merged. You may wish to delay the deletion for a few days in case it is needed for some reason, but when you are ready, the commands to delete the local and remote branches are: + +> [!IMPORTANT] +> Release branches start with `release/v`. Take care not to delete the tag, which starts with a `v`. + +```console +git branch -d +git push --delete +``` + +### Update Lucene.NET + +The Lucene.NET project is the only consumer of this package. If the release was intended for general use (not just a one-off scan), update the version in `Dependencies.props` to reflect the new release and submit a pull request to [the Lucene.NET repository](https://github.com/apache/lucenenet). diff --git a/docs/visual-studio-debugging.md b/docs/visual-studio-debugging.md new file mode 100644 index 0000000..346d410 --- /dev/null +++ b/docs/visual-studio-debugging.md @@ -0,0 +1,47 @@ + + +# Visual Studio Debugging + +Debugging the project in Visual Studio 2022 requires the [Microsoft Child Process Debugger Power Tool 2022](https://marketplace.visualstudio.com/items?itemName=vsdbgplat.MicrosoftChildProcessDebuggingPowerTool2022). This is a requirement because starting the debugger session creates multiple processes and we need to inform the debugger which one we want to attach to. The extension makes this automatic, since the settings will load from the `Lucene.Net.CodeAnalysis.Dev.ChildProcessDbgSettings` file in the solution directory. + +### Setting up Debugging Manually + +This is how the settings should be configured to debug a Roslyn analyzer when using Visual Studio 2022. + +1. Install the [Microsoft Child Process Debugger Power Tool 2022](https://marketplace.visualstudio.com/items?itemName=vsdbgplat.MicrosoftChildProcessDebuggingPowerTool2022) extension. +2. In the VSIX project, choose Properties and then select the Debug tab, ensure that native debugging is enabled. + + ![VS Native Debugging Settings](images/vs-native-debugging-setting.png) + +3. In the VS extension configuration available via Debug > Other Debug Targets > Child Process Debugging Settings..., set up the settings like the following. + + ![VS Child Process Debugging Settings](images/vs-child-process-debugging-settings.png) + +> **NOTE:** Since these settings are already configured in the repository, there should be no need to change them. + +### Starting Debugging + +Since the `Lucene.Net.CodeAnalysis.Dev.Vsix` project appears first in the solution file, it should be highlighted as the default project. If not, select it, right-click, and choose Select as Startup Project. + +Click on the play button or F5 to begin the debugging session. This will take a while for the first start, as it needs to download all native symbols first. However, this is a one-time startup cost. + +This launches a test instance of Visual Studio 2022. From there, you can create a new solution and use it to trigger various analyzer violations and walk through how the UX works for the user. You can also step into code, set and hit breakpoints. diff --git a/eng/Ensure-Powershell-Dependencies.ps1 b/eng/Ensure-Powershell-Dependencies.ps1 new file mode 100644 index 0000000..05c86fa --- /dev/null +++ b/eng/Ensure-Powershell-Dependencies.ps1 @@ -0,0 +1,59 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +param( + [string] $PesterVersion = "5.5.0" +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version Latest + +# Ensure NuGet provider exists +if (-not (Get-PackageProvider -Name NuGet -ErrorAction SilentlyContinue)) { + Install-PackageProvider -Name NuGet -Force -Scope CurrentUser | Out-Null +} + +# Ensure PSGallery is registered +$repo = Get-PSRepository -Name PSGallery -ErrorAction SilentlyContinue +if (-not $repo) { + Register-PSRepository -Name PSGallery -SourceLocation "https://www.powershellgallery.com/api/v2" -InstallationPolicy Untrusted + $repo = Get-PSRepository -Name PSGallery +} + +# Track original InstallationPolicy +$originalPolicy = $repo.InstallationPolicy +$restorePolicy = $false + +try { + if ($originalPolicy -ne 'Trusted') { + # Temporarily trust PSGallery + Set-PSRepository -Name PSGallery -InstallationPolicy Trusted + $restorePolicy = $true + } + + # Check if correct Pester version is installed + $module = Get-Module -ListAvailable -Name Pester | Sort-Object Version -Descending | Select-Object -First 1 + if (-not $module -or $module.Version -ne [version]$PesterVersion) { + Install-Module Pester -Scope CurrentUser -Force -SkipPublisherCheck -RequiredVersion $PesterVersion + } +} +finally { + if ($restorePolicy) { + # Restore original policy + Set-PSRepository -Name PSGallery -InstallationPolicy $originalPolicy + } +} diff --git a/eng/Lucene.Net.snk b/eng/Lucene.Net.snk new file mode 100644 index 0000000..f7f9ee5 Binary files /dev/null and b/eng/Lucene.Net.snk differ diff --git a/eng/WildcardVersionSupport.targets b/eng/WildcardVersionSupport.targets new file mode 100644 index 0000000..2fac11d --- /dev/null +++ b/eng/WildcardVersionSupport.targets @@ -0,0 +1,88 @@ + + + + + + + $(FileVersion) + + + false + + + $(AssemblyVersion) + + + false + + + + + + + + + <_Parameter1>$(GeneratedFileVersion) + + + + + + + + + + + <_Parameter1>$(GeneratedAssemblyVersion) + + + + + + + + + + + + + 0 && int.TryParse(parts[0], out var m) ? m : 1; + int minor = parts.Length > 1 && int.TryParse(parts[1], out var n) ? n : 0; + int buildPart = parts.Length > 2 && parts[2] != "*" ? int.Parse(parts[2]) : build; + int revPart = parts.Length > 3 && parts[3] != "*" ? int.Parse(parts[3]) : revision; + + OutputVersion = $"{major}.{minor}.{buildPart}.{revPart}"; + ]]> + + + + diff --git a/eng/build/Markdown-Formatting.Tests.ps1 b/eng/build/Markdown-Formatting.Tests.ps1 new file mode 100644 index 0000000..d815613 --- /dev/null +++ b/eng/build/Markdown-Formatting.Tests.ps1 @@ -0,0 +1,78 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +BeforeAll { + . $PSCommandPath.Replace('.Tests.ps1','.ps1') +} + +Describe "Format-Test-Results" { + $testCases = @( + @{ SuiteName="Alpha"; Passed=5; Failed=0; Ignored=16; Crashed=$false; Expected="✅ Passed" }, + @{ SuiteName="Beta"; Passed=1; Failed=2; Ignored=0; Crashed=$false; Expected="❌ Failed" }, + @{ SuiteName="Gamma"; Passed=0; Failed=0; Ignored=1; Crashed=$true; Expected="⚠️ Crashed" } + ) + + It "produces expected status lines" -ForEach $testCases { + $obj = [PSCustomObject]@{ + SuiteName = $_.SuiteName + PassedCount = $_.Passed + FailedCount = $_.Failed + IgnoredCount = $_.Ignored + Crashed = $_.Crashed + } + + $output = Format-Test-Results $obj + Write-Host $output -ForegroundColor Green + $output | Should -Match $_.Expected + $output | Should -Match "\*\*$($_.SuiteName)\*\*" + $output | Should -Match "Passed=$($_.PassedCount)" + $output | Should -Match "Failed=$($_.FailedCount)" + $output | Should -Match "Ignored=$($_.IgnoredCount)" + } + + Context "respects custom status text/icons" { + It "respects Crashed" { + $obj = [PSCustomObject]@{ + SuiteName="Delta"; PassedCount=0; FailedCount=0; IgnoredCount=0; Crashed=$true + } + + $output = Format-Test-Results $obj ` + -IconCrashed 'XX' -TextCrashed 'Boom' + $output | Should -Match "XX Boom" + } + + It "respects Passed" { + $obj = [PSCustomObject]@{ + SuiteName="Delta"; PassedCount=30; FailedCount=0; IgnoredCount=0; Crashed=$false + } + + $output = Format-Test-Results $obj ` + -IconPassed 'YY' -TextPassed 'MePassed' + $output | Should -Match "YY MePassed" + } + + It "respects Failed" { + $obj = [PSCustomObject]@{ + SuiteName="Delta"; PassedCount=30; FailedCount=2; IgnoredCount=0; Crashed=$false + } + + $output = Format-Test-Results $obj ` + -IconFailed 'ZZ' -TextFailed 'MeFailed' + $output | Should -Match "ZZ MeFailed" + } + } +} diff --git a/eng/build/Markdown-Formatting.ps1 b/eng/build/Markdown-Formatting.ps1 new file mode 100644 index 0000000..651e85d --- /dev/null +++ b/eng/build/Markdown-Formatting.ps1 @@ -0,0 +1,141 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +<# +.SYNOPSIS + Formats test results into a Markdown summary with icons and status text. + +.DESCRIPTION + The Format-Test-Results function takes one or more test result objects + (typically PSCustomObjects with fields such as SuiteName, PassedCount, + FailedCount, IgnoredCount, and Crashed) and produces a Markdown string + summarizing the results. Each test suite is displayed with an icon, + status text, and counts of passed, failed, and ignored tests. + + By default, the output includes a "## Test Results" heading and a bullet + point for each suite. + +.PARAMETER Results + One or more PSCustomObjects representing test results. Each object should + include the following properties: + - SuiteName [string] + - PassedCount [int] + - FailedCount [int] + - IgnoredCount [int] + - Crashed [bool] + + This parameter is mandatory and accepts input from the pipeline. + +.PARAMETER IconPassed + The icon to display for suites where all tests passed (default: ✅). + +.PARAMETER TextPassed + The label to display for passing suites (default: "Passed"). + +.PARAMETER IconFailed + The icon to display for suites with at least one failed test (default: ❌). + +.PARAMETER TextFailed + The label to display for failing suites (default: "Failed"). + +.PARAMETER IconCrashed + The icon to display for suites that crashed (default: ⚠️). + +.PARAMETER TextCrashed + The label to display for crashed suites (default: "Crashed"). + +.EXAMPLE + $results = @( + [pscustomobject]@{ SuiteName = "UnitTests"; PassedCount=10; FailedCount=0; IgnoredCount=0; Crashed=$false }, + [pscustomobject]@{ SuiteName = "IntegrationTests"; PassedCount=8; FailedCount=2; IgnoredCount=1; Crashed=$false }, + [pscustomobject]@{ SuiteName = "UITests"; PassedCount=0; FailedCount=0; IgnoredCount=0; Crashed=$true } + ) + + $results | Format-Test-Results + + Produces output similar to: + + ## Test Results + + - ✅ Passed - **UnitTests** | Passed=10, Failed=0, Ignored=0 + - ❌ Failed - **IntegrationTests** | Passed=8, Failed=2, Ignored=1 + - ⚠️ Crashed - **UITests** | Passed=0, Failed=0, Ignored=0 + +.EXAMPLE + $results | Format-Test-Results -IconFailed "💥" -TextFailed "Broken" + + Overrides the failed suite indicator with a custom icon and text. + +.OUTPUTS + System.String + Returns a Markdown-formatted string suitable for console output, + saving to a file, or inclusion in CI/CD summaries (e.g., GitHub Actions). + +.NOTES + The Markdown output is designed for human-readable summaries, + not for machine parsing. +#> + +function Format-Test-Results { + [CmdletBinding()] + param( + [Parameter(Mandatory, ValueFromPipeline, Position=0)] + [ValidateNotNullOrEmpty()] + [PSCustomObject[]] $Results, + + # Icons/texts as parameters (with defaults) + [string] $IconPassed = '✅', + [string] $TextPassed = 'Passed', + + [string] $IconFailed = '❌', + [string] $TextFailed = 'Failed', + + [string] $IconCrashed = '⚠️', + [string] $TextCrashed = 'Crashed' + ) + + begin { + $sb = [System.Text.StringBuilder]::new() + [void]$sb.AppendLine("## Test Results`n") + } + + process { + foreach ($r in $Results) { + if ($r.Crashed) { + $statusIcon = $IconCrashed + $statusText = $TextCrashed + } + elseif ($r.FailedCount -gt 0) { + $statusIcon = $IconFailed + $statusText = $TextFailed + } + else { + $statusIcon = $IconPassed + $statusText = $TextPassed + } + + $line = "- $statusIcon $statusText - **$($r.SuiteName)** " + + "| Passed=$($r.PassedCount), Failed=$($r.FailedCount), Ignored=$($r.IgnoredCount)" + + [void]$sb.AppendLine($line) + } + } + + end { + return $sb.ToString() + } +} diff --git a/eng/build/Parse-Test-Results.Tests.ps1 b/eng/build/Parse-Test-Results.Tests.ps1 new file mode 100644 index 0000000..1489fcf --- /dev/null +++ b/eng/build/Parse-Test-Results.Tests.ps1 @@ -0,0 +1,233 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +$global:TempTestDir = $null + +Describe "Parse-Test-Results" { + BeforeAll { + # Create a single temp directory for all tests + $global:TempTestDir = Join-Path ([IO.Path]::GetTempPath()) ([Guid]::NewGuid()) + New-Item -ItemType Directory -Path $global:TempTestDir | Out-Null + + # Helper function to invoke the script under test + function Parse-Test-Results { + param( + [Parameter(Position = 0)] + [string]$Path + ) + & $PSCommandPath.Replace('.Tests.ps1','.ps1') $Path + } + + # Helper function to create a TRX file in the temp folder + function New-TrxFile { + param( + [string]$Content, + [string]$FileName = "$(New-Guid).trx" + ) + + $filePath = Join-Path $global:TempTestDir $FileName + $Content | Set-Content -Path $filePath -Encoding UTF8 + return $filePath + } + } + + It "parses a passed run" { + # Arrange + $trxContent = @" + + + + + + +"@ + $trxPath = New-TrxFile -Content $trxContent -FileName 'passed.trx' + + # Act + $result = Parse-Test-Results -Path $trxPath + + # Assert + $result.PassedCount | Should -Be 3 + $result.FailedCount | Should -Be 0 + $result.IgnoredCount | Should -Be 0 + $result.Crashed | Should -Be $false + } + + It "parses a failed run" { + # Arrange + $trxContent = @" + + + + + + +"@ + $trxPath = New-TrxFile -Content $trxContent -FileName 'failed.trx' + + # Act + $result = Parse-Test-Results -Path $trxPath + + # Assert + $result.PassedCount | Should -Be 2 + $result.FailedCount | Should -Be 1 + $result.IgnoredCount | Should -Be 0 + $result.Crashed | Should -Be $false + } + + It "calculates ignored test count" { + # Arrange + $trxContent = @" + + + + + + +"@ + $trxPath = New-TrxFile -Content $trxContent -FileName 'failed.trx' + + # Act + $result = Parse-Test-Results -Path $trxPath + + # Assert + $result.PassedCount | Should -Be 2 + $result.FailedCount | Should -Be 1 + $result.IgnoredCount | Should -Be 4 + $result.Crashed | Should -Be $false + } + + Context "detects a crash" { + It "could not find dotnet" { + # Arrange + $trxContent = @" + + + + + + + + Could not find 'dotnet.exe' host + + + +"@ + $trxPath = New-TrxFile -Content $trxContent -FileName 'crashed-could-not-find-dotnet.trx' + + # Act + $result = Parse-Test-Results -Path $trxPath + + # Assert + $result.PassedCount | Should -Be 0 + $result.FailedCount | Should -Be 0 + $result.IgnoredCount | Should -Be 0 + $result.Crashed | Should -Be $true + } + + It "could not load assembly" { + # Arrange + $trxContent = @" + + + + + + + + Could not load file or assembly 'foo.dll' + + + +"@ + $trxPath = New-TrxFile -Content $trxContent -FileName 'crashed-could-not-load-assembly.trx' + + # Act + $result = Parse-Test-Results -Path $trxPath + + # Assert + $result.PassedCount | Should -Be 0 + $result.FailedCount | Should -Be 0 + $result.IgnoredCount | Should -Be 0 + $result.Crashed | Should -Be $true + } + + It "exited with error" { + # Arrange + $trxContent = @" + + + + + + + + The program exited with error 1234. + + + +"@ + $trxPath = New-TrxFile -Content $trxContent -FileName 'crashed-exited-with-error.trx' + + # Act + $result = Parse-Test-Results -Path $trxPath + + # Assert + $result.PassedCount | Should -Be 0 + $result.FailedCount | Should -Be 0 + $result.IgnoredCount | Should -Be 0 + $result.Crashed | Should -Be $true + } + + It "no test is available" { + # Arrange + $trxContent = @" + + + + + + + + No test is available in the assembly + + + +"@ + $trxPath = New-TrxFile -Content $trxContent -FileName 'crashed-no-test-is-available.trx' + + # Act + $result = Parse-Test-Results -Path $trxPath + + # Assert + $result.PassedCount | Should -Be 0 + $result.FailedCount | Should -Be 0 + $result.IgnoredCount | Should -Be 0 + $result.Crashed | Should -Be $true + } + } + + AfterAll { + # Clean up temp directory + if ($global:TempTestDir -and (Test-Path $global:TempTestDir)) { + Remove-Item -Path $global:TempTestDir -Recurse -Force + } + + # Perform cleanup based on the variable's value + $global:TempTestDir = $null # Reset for subsequent runs if needed + } +} diff --git a/eng/build/Parse-Test-Results.ps1 b/eng/build/Parse-Test-Results.ps1 new file mode 100644 index 0000000..67af2ca --- /dev/null +++ b/eng/build/Parse-Test-Results.ps1 @@ -0,0 +1,111 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +<# +.SYNOPSIS + Parses a Visual Studio TRX test results file and summarizes the test outcomes. + +.DESCRIPTION + This script reads a TRX (Test Result XML) file produced by Visual Studio or + `dotnet test` and extracts key information about the test run. It calculates + the number of passed, failed, and ignored tests, and detects if the test + run crashed based on specific error messages in the TRX file. + +.PARAMETER Path + The path to the results file to parse. The script throws an error if the file + does not exist. Position 0. + +.EXAMPLE + $result = .\Parse-Test-Results.ps1 -Path "C:\temp\testresults.trx" + + Returns a PSCustomObject with properties: + Passed - Number of passed tests + Failed - Number of failed tests + Ignored - Number of ignored/skipped tests + Crashed - Boolean indicating if the test run crashed + +.NOTES + - Requires PowerShell 5.x or later. + - Stops execution on any errors and uses strict mode for variable usage. + - Designed to be used in CI/CD pipelines or automated test scripts. + +.OUTPUTS + PSCustomObject with properties: + - Passed [int] + - Failed [int] + - Ignored [int] + - Crashed [bool] + +#> +param( + [Parameter(Position = 0)] + [string]$Path +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version Latest + +if (-not (Test-Path $Path)) { + throw "File not found: $Path" +} + +$reader = [System.Xml.XmlReader]::Create($Path) +try { + [bool]$countersFound = $false + [bool]$inRunInfos = $false + [bool]$crashed = $false + [int]$failedCount = 0 + [int]$passedCount = 0 + [int]$ignoredCount = 0 + + while ($reader.Read()) { + if ($reader.NodeType -eq [System.Xml.XmlNodeType]::Element) { + if (!$countersFound -and $reader.Name -eq 'Counters') { + $failedCount = [int]$reader.GetAttribute('failed') + $passedCount = [int]$reader.GetAttribute('passed') + $ignoredCount = [int]$reader.GetAttribute('total') - [int]$reader.GetAttribute('executed') + $countersFound = $true + } + if ($reader.Name -eq 'RunInfos') { $inRunInfos = $true } + if ($inRunInfos -and !$crashed -and $reader.Name -eq 'Text') { + $innerXml = $reader.ReadInnerXml() + if ($innerXml -and ( + $innerXml.Contains('Test host process crashed') -or + $innerXml.Contains('Could not load file or assembly') -or + $innerXml.Contains("Could not find `'dotnet.exe`' host") -or + $innerXml.Contains('No test is available') -or + $innerXml.Contains('exited with error') + )) { + $crashed = $true + } + } + } + if ($reader.NodeType -eq [System.Xml.XmlNodeType]::EndElement -and $reader.Name -eq 'RunInfos') { + $inRunInfos = $false + } + } +} +finally { + $reader.Dispose() +} + +[PSCustomObject]@{ + PassedCount = $passedCount + FailedCount = $failedCount + IgnoredCount = $ignoredCount + Crashed = $crashed +} diff --git a/eng/build/coverage.runsettings b/eng/build/coverage.runsettings new file mode 100644 index 0000000..cdd999f --- /dev/null +++ b/eng/build/coverage.runsettings @@ -0,0 +1,100 @@ + + + + + + + + + .*\.dll$ + .*\.exe$ + + + + .*\.Tests(\.|$).* + + + .*NUnit3\.TestAdapter.* + + + .*Microsoft\.CodeAnalysis\.Analyzer\.Testing.* + + + + + + + + ^System\.Diagnostics\.DebuggerHiddenAttribute$ + ^System\.Diagnostics\.DebuggerNonUserCodeAttribute$ + ^System\.CodeDom\.Compiler\.GeneratedCodeAttribute$ + ^System\.Diagnostics\.CodeAnalysis\.ExcludeFromCodeCoverageAttribute$ + + + + + + + .*[Mm]icrosoft.* + + + + + + + + + ^B77A5C561934E089$ + ^b77a5c561934e089$ + + + ^B03F5F7F11D50A3A$ + ^b03f5f7f11d50a3a$ + + + ^31BF3856AD364E35$ + ^31bf3856ad364e35$ + + + ^89845DCD8080CC91$ + ^89845dcd8080cc91$ + + + ^71E9BCE111E9429C$ + ^71e9bce111e9429c$ + + + ^8F50407C4E9E73B6$ + ^8f50407c4e9e73b6$ + + + ^E361AF139669C375$ + ^e361af139669c375$ + + + + + + + False + + True + False + False + + False + False + diff --git a/eng/eng.folderproj b/eng/eng.folderproj new file mode 100644 index 0000000..b5711dc --- /dev/null +++ b/eng/eng.folderproj @@ -0,0 +1,25 @@ + + + + + + \ No newline at end of file diff --git a/eng/git-hooks/post-commit b/eng/git-hooks/post-commit new file mode 100755 index 0000000..5918708 --- /dev/null +++ b/eng/git-hooks/post-commit @@ -0,0 +1,71 @@ +#!/bin/sh +# eng/git-hooks/post-commit +# Replaces {{vnext}} in "## Release {{vnext}}" with the NuGetPackageVersion +# only if the file contains the token. Safe from infinite loops. + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +file="src/Lucene.Net.CodeAnalysis.Dev/AnalyzerReleases.Shipped.md" +token="{{vnext}}" + +# Bail if already running to prevent infinite loop +if [ -n "$POST_COMMIT_RUNNING" ]; then + exit 0 +fi + +# Bail if the file doesn't exist +[ ! -f "$file" ] && exit 0 + +# Bail if the token doesn't exist in the file +if ! grep -q "## Release $token" "$file"; then + exit 0 +fi + +# Check if nbgv tool is installed +if ! command -v nbgv >/dev/null 2>&1; then + cat < + + + true + The Apache Software Foundation + https://lucenenet.apache.org + lucene-net-icon-128x128.png + Apache-2.0 + readme.md + NOTICE.txt + https://github.com/$(GitHubOrganization)/$(GitHubProject)/releases/tag/v$(PackageVersion) + $(ReleaseNotesUrl) + + + + + + + + diff --git a/global.json b/global.json new file mode 100644 index 0000000..8d6478c --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "msbuild-sdks": { + "Microsoft.Build.NoTargets": "3.7.56" + }, + "sources": [ "src" ] +} diff --git a/rat.ps1 b/rat.ps1 new file mode 100644 index 0000000..15dd4c6 --- /dev/null +++ b/rat.ps1 @@ -0,0 +1,107 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +<# +.SYNOPSIS + Helper script to download and run the Apache Release Audit Tool (RAT). + +.DESCRIPTION + This script automates use of the Apache RAT tool during release preparation. + It ensures that the RAT JAR is available locally (downloading it if needed) + and then runs it against the source tree to check for missing license headers + and other compliance issues. + + By default, the tool is downloaded into a `.tools/rat` directory located next + to this script. The script always runs RAT with the repository root (same + directory as this script) as the target directory. + +.PARAMETER Version + The version of Apache RAT to use (default: 0.13). + +.PARAMETER ExcludeFileName + Name of an exclude file containing path patterns that RAT should ignore. + This file should be located next to the script. Default: rat-exclude.txt + +.REQUIREMENTS + - Java 8+ must be installed and available on the PATH. + - Internet connection (for first run, to download RAT). + +.EXAMPLE + pwsh ./rat.ps1 + + Runs Apache RAT (default version 0.13) with exclusions from `rat-exclude.txt`. + +.EXAMPLE + pwsh ./rat.ps1 -Version 0.13 -ExcludeFileName custom-exclude.txt + + Runs Apache RAT version 0.13 using the specified exclude file. + +.NOTES + This script is intended for use by release managers when preparing official + ASF releases. It is not normally required for day-to-day development. +#> +param( + [string]$Version = "0.13", + [string]$ExcludeFileName = ".rat-excludes" +) + +# Script directory (works in PowerShell Core and Windows PowerShell) +$scriptDir = $PSScriptRoot + +# Tool paths (kept under the script dir) +$ratDir = Join-Path $scriptDir ".tools\rat" +$ratJar = Join-Path $ratDir "apache-rat-$Version.jar" +$ratUrl = "https://repo1.maven.org/maven2/org/apache/rat/apache-rat/$Version/apache-rat-$Version.jar" + +# Exclude file path (resolved relative to script dir) +$ratExcludeFile = Join-Path $scriptDir $ExcludeFileName + +# Ensure tool folder exists and jar is present (download if missing) +if (-not (Test-Path $ratDir)) { + New-Item -ItemType Directory -Path $ratDir | Out-Null +} + +if (-not (Test-Path $ratJar)) { + Write-Host "Downloading Apache RAT $Version to $ratJar ..." + Invoke-WebRequest -Uri $ratUrl -OutFile $ratJar +} + +# If exclude file is optional, optionally warn if missing: +if (-not (Test-Path $ratExcludeFile)) { + Write-Host "Warning: exclude file '$ratExcludeFile' not found. Continuing without --exclude-file." + $useExclude = $false +} else { + $useExclude = $true +} + +$argsList = @( + "-jar", $ratJar, + "--dir", "`"$scriptDir`"", + "--addLicense", + "--force" +) + +if ($useExclude) { + $argsList += @("--exclude-file", "`"$ratExcludeFile`"") +} + +# Call java with argument list. Use & to invoke program. +& java @argsList + +if ($LASTEXITCODE -ne 0) { + throw "RAT exited with code $LASTEXITCODE" +} diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..b35268e --- /dev/null +++ b/renovate.json @@ -0,0 +1,171 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended", + ":dependencyDashboard", + ":prConcurrentLimit10", + ":prHourlyLimitNone" + ], + "enabledManagers": [ + "nuget", + "github-actions", + "custom.regex" + ], + "rangeStrategy": "bump", + "schedule": [], + "commitMessagePrefix": "chore: ", + "customManagers": [ + { + "customType": "regex", + "description": "Roslyn Analyzer package version property", + "managerFilePatterns": [ + "/(^|.*/)Directory\\.Packages\\.props$/" + ], + "matchStrings": [ + "<\\s*RoslynAnalyzerPackageVersion\\s*>(?[^<]+)<\\/\\s*RoslynAnalyzerPackageVersion\\s*>" + ], + "depNameTemplate": "Microsoft.CodeAnalysis.Common", + "datasourceTemplate": "nuget", + "versioningTemplate": "nuget" + } + ], + "packageRules": [ + { + "groupName": "Test Dependencies", + "groupSlug": "test-dependencies", + "matchManagers": [ + "nuget" + ], + "matchPackageNames": [ + "NUnit", + "NUnit3TestAdapter", + "Microsoft.NET.Test.Sdk" + ], + "labels": [ + "dependencies", + "test-deps", + "notes:ignore" + ] + }, + { + "groupName": "Roslyn Compiler Packages", + "groupSlug": "roslyn-compiler", + "matchManagers": [ + "custom.regex" + ], + "matchPackageNames": [ + "Microsoft.CodeAnalysis.Common", + "Microsoft.CodeAnalysis.CSharp", + "Microsoft.CodeAnalysis.CSharp.Features", + "Microsoft.CodeAnalysis.CSharp.Workspaces", + "Microsoft.CodeAnalysis.VisualBasic", + "Microsoft.CodeAnalysis.VisualBasic.Features", + "Microsoft.CodeAnalysis.VisualBasic.Workspaces", + "Microsoft.CodeAnalysis.Workspaces.Common" + ], + "matchUpdateTypes": [ + "minor", + "patch" + ], + "labels": [ + "dependencies", + "roslyn", + "notes:ignore" + ] + }, + { + "groupName": "Roslyn Analyzer Testing", + "groupSlug": "roslyn-analyzer-testing", + "matchManagers": [ + "nuget" + ], + "matchPackageNames": [ + "Microsoft.CodeAnalysis.Analyzer.Testing" + ], + "labels": [ + "dependencies", + "roslyn", + "notes:ignore" + ] + }, + { + "groupName": "System.Memory Dependencies", + "matchPackageNames": [ + "System.Memory", + "System.Buffers", + "System.Runtime.CompilerServices.Unsafe", + "System.Numerics.Vectors" + ], + "matchManagers": [ + "nuget" + ], + "enabled": false, + "labels": [ + "dependencies", + "manual-upgrade", + "notes:ignore" + ] + }, + { + "groupName": "Visual Studio SDK Build Tools", + "matchPackageNames": [ + "Microsoft.VSSDK.BuildTools" + ], + "matchManagers": [ + "nuget" + ], + "enabled": false, + "labels": [ + "dependencies", + "manual-upgrade", + "notes:ignore" + ] + }, + { + "groupName": "Analyzer Dependencies", + "groupSlug": "roslyn-analyzers", + "matchManagers": [ + "nuget" + ], + "matchDepNames": [ + "/.*Analyzers$/" + ], + "matchUpdateTypes": [ + "major", + "minor", + "patch" + ], + "labels": [ + "dependencies", + "analyzers", + "notes:ignore" + ] + }, + { + "groupName": "GitHub Actions", + "groupSlug": "github-actions-all", + "matchManagers": [ + "github-actions" + ], + "pinDigests": true, + "rangeStrategy": "replace", + "prCreation": "immediate", + "semanticCommits": "enabled", + "commitMessageTopic": "GitHub Action {{depName}}", + "labels": [ + "dependencies", + "ci", + "notes:ignore" + ] + }, + { + "matchFileNames": [ + "global.json" + ], + "enabled": false + } + ], + "automerge": false, + "recreateWhen": "always", + "dependencyDashboard": true +} diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 0000000..67c7a9d --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,23 @@ + + + + diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets new file mode 100644 index 0000000..ebdaddf --- /dev/null +++ b/src/Directory.Build.targets @@ -0,0 +1,29 @@ + + + + + + bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml + $(NoWarn);CS1591 + + + diff --git a/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/CodeFixResources.Designer.cs b/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/CodeFixResources.Designer.cs deleted file mode 100644 index 76dad94..0000000 --- a/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/CodeFixResources.Designer.cs +++ /dev/null @@ -1,83 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.0 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -using System; -using System.Reflection; - -namespace Lucene.Net.CodeAnalysis.Dev -{ - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class CodeFixResources - { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal CodeFixResources() - { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if (object.ReferenceEquals(resourceMan, null)) - { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Lucene.Net.CodeAnalysis.Dev.CodeFixResources", typeof(CodeFixResources).GetTypeInfo().Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { - return resourceCulture; - } - set - { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to Make uppercase. - /// - internal static string CodeFixTitle - { - get - { - return ResourceManager.GetString("CodeFixTitle", resourceCulture); - } - } - } -} diff --git a/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/CodeFixResources.resx b/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/CodeFixResources.resx index 97abe68..d4db943 100644 --- a/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/CodeFixResources.resx +++ b/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/CodeFixResources.resx @@ -1,17 +1,37 @@ - + + + + - + --> - - - - - - - - - - - - - - - - - - - - + + - - - - + + + - + - + @@ -109,16 +110,16 @@ text/microsoft-resx - 2.0 + 1.3 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Make uppercase - The title of the code fix. + + Use {0} + Title for code fix; {0} is the code element to utilize. - \ No newline at end of file + diff --git a/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/Lucene.Net.CodeAnalysis.Dev.CodeFixes.csproj b/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/Lucene.Net.CodeAnalysis.Dev.CodeFixes.csproj index 67ce140..f38ca60 100644 --- a/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/Lucene.Net.CodeAnalysis.Dev.CodeFixes.csproj +++ b/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/Lucene.Net.CodeAnalysis.Dev.CodeFixes.csproj @@ -1,20 +1,66 @@ + + + netstandard2.0 - false - Lucene.Net.CodeAnalysis.Dev + enable + + + false - 1.0.2 + + + PrepareResources;$(CompileDependsOn) + true + en-US - + + + MSBuild:Compile + $(IntermediateOutputPath)CodeFixResources.designer.cs + CSharp + Lucene.Net.CodeAnalysis.Dev.CodeFixes + CodeFixResources + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + - - + diff --git a/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/LuceneDev1xxx/LuceneDev1001_FloatingPointFormattingCSCodeFixProvider.cs b/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/LuceneDev1xxx/LuceneDev1001_FloatingPointFormattingCSCodeFixProvider.cs new file mode 100644 index 0000000..7d278ee --- /dev/null +++ b/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/LuceneDev1xxx/LuceneDev1001_FloatingPointFormattingCSCodeFixProvider.cs @@ -0,0 +1,160 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Lucene.Net.CodeAnalysis.Dev.CodeFixes.Utility; +using Lucene.Net.CodeAnalysis.Dev.Utility; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Formatting; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Lucene.Net.CodeAnalysis.Dev.CodeFixes +{ + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(LuceneDev1001_FloatingPointFormattingCSCodeFixProvider)), Shared] + public class LuceneDev1001_FloatingPointFormattingCSCodeFixProvider : CodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds => + [Descriptors.LuceneDev1001_FloatingPointFormatting.Id]; + + public override FixAllProvider GetFixAllProvider() => + WellKnownFixAllProviders.BatchFixer; + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + Diagnostic? diagnostic = context.Diagnostics.FirstOrDefault(); + if (diagnostic is null) + return; + + SyntaxNode? root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + if (root is null) + return; + + // the diagnostic in the analyzer is reported on the member access (e.g. "x.ToString") + // but we need the whole invocation (e.g. "x.ToString(...)"). So find the invocation + // by walking ancestors if needed. + SyntaxNode? node = root.FindNode(diagnostic.Location.SourceSpan); + if (node is null) + return; + + if (node is not ExpressionSyntax exprNode) + return; + + SemanticModel? semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); + if (semanticModel is null) + return; + + if (!TryGetJ2NTypeAndMember(semanticModel, exprNode, out var j2nTypeName, out var memberAccess)) + return; + + string codeElement = $"J2N.Numerics.{j2nTypeName}.ToString(...)"; + + context.RegisterCodeFix( + CodeActionHelper.CreateFromResource( + CodeFixResources.UseX, + c => ReplaceWithJ2NToStringAsync(context.Document, memberAccess, c), + "UseJ2NToString", + codeElement), + diagnostic); + } + + private async Task ReplaceWithJ2NToStringAsync( + Document document, + MemberAccessExpressionSyntax memberAccess, + CancellationToken cancellationToken) + { + SemanticModel? semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + if (semanticModel is null) + return document; + + if (!TryGetJ2NTypeAndMember(semanticModel, memberAccess, out var j2nTypeName, out _)) + return document; + + // Build J2N.Numerics.Single/Double.ToString + MemberAccessExpressionSyntax j2nToStringAccess = SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName("J2N"), + SyntaxFactory.IdentifierName("Numerics")), + SyntaxFactory.IdentifierName(j2nTypeName)) + .WithAdditionalAnnotations(Formatter.Annotation); + + MemberAccessExpressionSyntax fullAccess = SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + j2nToStringAccess, + SyntaxFactory.IdentifierName("ToString")); + + // Build invocation: J2N.Numerics..ToString(, ) + if (memberAccess.Parent is not InvocationExpressionSyntax invocation) + return document; + + var newArgs = new List { SyntaxFactory.Argument(memberAccess.Expression) }; + if (invocation.ArgumentList != null) + newArgs.AddRange(invocation.ArgumentList.Arguments); + + InvocationExpressionSyntax newInvocation = SyntaxFactory.InvocationExpression( + fullAccess, + SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(newArgs))) + .WithTriviaFrom(invocation) // safe now + .WithAdditionalAnnotations(Formatter.Annotation); + + DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + editor.ReplaceNode(memberAccess.Parent, newInvocation); + + return editor.GetChangedDocument(); + } + + private static bool TryGetJ2NTypeAndMember( + SemanticModel semanticModel, + ExpressionSyntax expr, + out string j2nTypeName, + out MemberAccessExpressionSyntax memberAccess) + { + memberAccess = expr as MemberAccessExpressionSyntax + ?? expr.AncestorsAndSelf().OfType().FirstOrDefault(); + + if (memberAccess is null) + { + j2nTypeName = null!; // we always return false when the value is null, so we can ignore it here. + return false; + } + + var typeInfo = semanticModel.GetTypeInfo(memberAccess.Expression); + var type = typeInfo.Type; + + j2nTypeName = type?.SpecialType switch + { + SpecialType.System_Single => "Single", + SpecialType.System_Double => "Double", + _ => null! // we always return false when the value is null, so we can ignore it here. + }; + + if (j2nTypeName is null) + return false; + + return true; + } + } +} diff --git a/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/LuceneNetCodeAnalysisDevCodeFixProvider.cs b/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/LuceneNetCodeAnalysisDevCodeFixProvider.cs deleted file mode 100644 index 9cd6ad8..0000000 --- a/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/LuceneNetCodeAnalysisDevCodeFixProvider.cs +++ /dev/null @@ -1,72 +0,0 @@ -//using Microsoft.CodeAnalysis; -//using Microsoft.CodeAnalysis.CodeActions; -//using Microsoft.CodeAnalysis.CodeFixes; -//using Microsoft.CodeAnalysis.CSharp; -//using Microsoft.CodeAnalysis.CSharp.Syntax; -//using Microsoft.CodeAnalysis.Rename; -//using Microsoft.CodeAnalysis.Text; -//using System; -//using System.Collections.Generic; -//using System.Collections.Immutable; -//using System.Composition; -//using System.Linq; -//using System.Threading; -//using System.Threading.Tasks; - -//namespace Lucene.Net.CodeAnalysis.Dev -//{ -// //[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(LuceneNetCodeAnalysisDevCodeFixProvider)), Shared] -// public class LuceneNetCodeAnalysisDevCodeFixProvider : CodeFixProvider -// { -// public sealed override ImmutableArray FixableDiagnosticIds -// { -// get { return ImmutableArray.Create(LuceneNetCodeAnalysisDevAnalyzer.DiagnosticId); } -// } - -// public sealed override FixAllProvider GetFixAllProvider() -// { -// // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for more information on Fix All Providers -// return WellKnownFixAllProviders.BatchFixer; -// } - -// public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) -// { -// var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); - -// // TODO: Replace the following code with your own analysis, generating a CodeAction for each fix to suggest -// var diagnostic = context.Diagnostics.First(); -// var diagnosticSpan = diagnostic.Location.SourceSpan; - -// // Find the type declaration identified by the diagnostic. -// var declaration = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf().OfType().First(); - - -// // Register a code action that will invoke the fix. -// context.RegisterCodeFix( -// CodeAction.Create( -// title: CodeFixResources.CodeFixTitle, -// createChangedSolution: c => MakeUppercaseAsync(context.Document, declaration, c), -// equivalenceKey: nameof(CodeFixResources.CodeFixTitle)), -// diagnostic); -// } - -// private async Task MakeUppercaseAsync(Document document, TypeDeclarationSyntax typeDecl, CancellationToken cancellationToken) -// { -// // Compute new uppercase name. -// var identifierToken = typeDecl.Identifier; -// var newName = identifierToken.Text.ToUpperInvariant(); - -// // Get the symbol representing the type to be renamed. -// var semanticModel = await document.GetSemanticModelAsync(cancellationToken); -// var typeSymbol = semanticModel.GetDeclaredSymbol(typeDecl, cancellationToken); - -// // Produce a new solution that has all references to that type renamed, including the declaration. -// var originalSolution = document.Project.Solution; -// var optionSet = originalSolution.Workspace.Options; -// var newSolution = await Renamer.RenameSymbolAsync(document.Project.Solution, typeSymbol, newName, optionSet, cancellationToken).ConfigureAwait(false); - -// // Return the new solution with the now-uppercase type name. -// return newSolution; -// } -// } -//} diff --git a/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/Utility/CodeActionHelper.cs b/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/Utility/CodeActionHelper.cs new file mode 100644 index 0000000..e9a68c2 --- /dev/null +++ b/src/Lucene.Net.CodeAnalysis.Dev.CodeFixes/Utility/CodeActionHelper.cs @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Lucene.Net.CodeAnalysis.Dev.CodeFixes.Utility +{ + internal static class CodeActionHelper + { + /// + /// Create a CodeAction using a resource string and formatting arguments. + /// + public static CodeAction CreateFromResource( + string resourceValue, + Func> createChangedDocument, + string equivalenceKey, + params object[] args) + { + var title = string.Format(resourceValue, args); + return CodeAction.Create(title, createChangedDocument, equivalenceKey); + } + } +} diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Package/Lucene.Net.CodeAnalysis.Dev.Package.csproj b/src/Lucene.Net.CodeAnalysis.Dev.Package/Lucene.Net.CodeAnalysis.Dev.Package.csproj index b64fa41..3354019 100644 --- a/src/Lucene.Net.CodeAnalysis.Dev.Package/Lucene.Net.CodeAnalysis.Dev.Package.csproj +++ b/src/Lucene.Net.CodeAnalysis.Dev.Package/Lucene.Net.CodeAnalysis.Dev.Package.csproj @@ -1,40 +1,126 @@ + + + + + + netstandard2.0 - false - true true + false + false + + true + true - + Lucene.Net.CodeAnalysis.Dev - 1.0.0.0 - shad - http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE - http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE - http://ICON_URL_HERE_OR_DELETE_THIS_LINE - http://REPOSITORY_URL_HERE_OR_DELETE_THIS_LINE - false - Lucene.Net.CodeAnalysis.Dev - Summary of changes made in this release of the package. - Copyright - Lucene.Net.CodeAnalysis.Dev, analyzers - true - true + Analyzers and code fixes for Lucene.NET development. + $(PackageTags);code analysis;code maintenance;roslyn + $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\_artifacts\NuGetPackages\$(Configuration)')) + <_PackageVersionPropsFile>$(PackageOutputPath)\Lucene.Net.CodeAnalysis.Dev.Version.props + - $(TargetsForTfmSpecificContentInPackage);_AddAnalyzersToOutput + + <_CodeAnalysisCSAssemblyFile>$(RepositoryRoot)src\Lucene.Net.CodeAnalysis.Dev\bin\$(Configuration)\$(TargetFramework)\Lucene.Net.CodeAnalysis.Dev.dll + <_CodeAnalysisCodeFixesCSAssemblyFile>$(RepositoryRoot)src\Lucene.Net.CodeAnalysis.Dev.CodeFixes\bin\$(Configuration)\$(TargetFramework)\Lucene.Net.CodeAnalysis.Dev.CodeFixes.dll + + <_CodeAnalyisisResources Include="$(RepositoryRoot)src\Lucene.Net.CodeAnalysis.Dev\bin\$(Configuration)\$(TargetFramework)\**\*.resources.dll" /> + + + + + + + + + + - + + + - + + + + + + + + + + + + + + + + + + + + + + + + $(PackageVersion)-debug-$([System.DateTime]::UtcNow.DayOfYear)-$([System.Math]::Floor($([System.DateTime]::UtcNow.TimeOfDay.TotalSeconds))) + + + + + + + + + + + - - + <__NuGetFilePaths Include="$(PackageOutputPath)\*.nupkg" /> + + diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Package/tools/install.ps1 b/src/Lucene.Net.CodeAnalysis.Dev.Package/tools/install.ps1 deleted file mode 100644 index 42c5309..0000000 --- a/src/Lucene.Net.CodeAnalysis.Dev.Package/tools/install.ps1 +++ /dev/null @@ -1,250 +0,0 @@ -param($installPath, $toolsPath, $package, $project) - -if($project.Object.SupportsPackageDependencyResolution) -{ - if($project.Object.SupportsPackageDependencyResolution()) - { - # Do not install analyzers via install.ps1, instead let the project system handle it. - return - } -} - -$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve - -foreach($analyzersPath in $analyzersPaths) -{ - if (Test-Path $analyzersPath) - { - # Install the language agnostic analyzers. - foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll) - { - if($project.Object.AnalyzerReferences) - { - $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName) - } - } - } -} - -# $project.Type gives the language name like (C# or VB.NET) -$languageFolder = "" -if($project.Type -eq "C#") -{ - $languageFolder = "cs" -} -if($project.Type -eq "VB.NET") -{ - $languageFolder = "vb" -} -if($languageFolder -eq "") -{ - return -} - -foreach($analyzersPath in $analyzersPaths) -{ - # Install language specific analyzers. - $languageAnalyzersPath = join-path $analyzersPath $languageFolder - if (Test-Path $languageAnalyzersPath) - { - foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll) - { - if($project.Object.AnalyzerReferences) - { - $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName) - } - } - } -} -# SIG # Begin signature block -# MIIjhgYJKoZIhvcNAQcCoIIjdzCCI3MCAQExDzANBglghkgBZQMEAgEFADB5Bgor -# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG -# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA/i+qRUHsWzI0s -# FVk99zLgt/HOEQ33uvkFsWtHTHZgf6CCDYEwggX/MIID56ADAgECAhMzAAAB32vw -# LpKnSrTQAAAAAAHfMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD -# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy -# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p -# bmcgUENBIDIwMTEwHhcNMjAxMjE1MjEzMTQ1WhcNMjExMjAyMjEzMTQ1WjB0MQsw -# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u -# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy -# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB -# AQC2uxlZEACjqfHkuFyoCwfL25ofI9DZWKt4wEj3JBQ48GPt1UsDv834CcoUUPMn -# s/6CtPoaQ4Thy/kbOOg/zJAnrJeiMQqRe2Lsdb/NSI2gXXX9lad1/yPUDOXo4GNw -# PjXq1JZi+HZV91bUr6ZjzePj1g+bepsqd/HC1XScj0fT3aAxLRykJSzExEBmU9eS -# yuOwUuq+CriudQtWGMdJU650v/KmzfM46Y6lo/MCnnpvz3zEL7PMdUdwqj/nYhGG -# 3UVILxX7tAdMbz7LN+6WOIpT1A41rwaoOVnv+8Ua94HwhjZmu1S73yeV7RZZNxoh -# EegJi9YYssXa7UZUUkCCA+KnAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE -# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUOPbML8IdkNGtCfMmVPtvI6VZ8+Mw -# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 -# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDYzMDA5MB8GA1UdIwQYMBaAFEhu -# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu -# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w -# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 -# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx -# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAnnqH -# tDyYUFaVAkvAK0eqq6nhoL95SZQu3RnpZ7tdQ89QR3++7A+4hrr7V4xxmkB5BObS -# 0YK+MALE02atjwWgPdpYQ68WdLGroJZHkbZdgERG+7tETFl3aKF4KpoSaGOskZXp -# TPnCaMo2PXoAMVMGpsQEQswimZq3IQ3nRQfBlJ0PoMMcN/+Pks8ZTL1BoPYsJpok -# t6cql59q6CypZYIwgyJ892HpttybHKg1ZtQLUlSXccRMlugPgEcNZJagPEgPYni4 -# b11snjRAgf0dyQ0zI9aLXqTxWUU5pCIFiPT0b2wsxzRqCtyGqpkGM8P9GazO8eao -# mVItCYBcJSByBx/pS0cSYwBBHAZxJODUqxSXoSGDvmTfqUJXntnWkL4okok1FiCD -# Z4jpyXOQunb6egIXvkgQ7jb2uO26Ow0m8RwleDvhOMrnHsupiOPbozKroSa6paFt -# VSh89abUSooR8QdZciemmoFhcWkEwFg4spzvYNP4nIs193261WyTaRMZoceGun7G -# CT2Rl653uUj+F+g94c63AhzSq4khdL4HlFIP2ePv29smfUnHtGq6yYFDLnT0q/Y+ -# Di3jwloF8EWkkHRtSuXlFUbTmwr/lDDgbpZiKhLS7CBTDj32I0L5i532+uHczw82 -# oZDmYmYmIUSMbZOgS65h797rj5JJ6OkeEUJoAVwwggd6MIIFYqADAgECAgphDpDS -# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK -# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 -# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 -# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla -# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS -# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT -# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB -# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG -# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S -# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz -# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 -# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u -# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 -# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl -# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP -# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB -# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF -# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM -# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ -# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud -# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO -# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 -# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y -# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p -# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y -# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB -# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw -# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA -# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY -# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj -# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd -# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ -# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf -# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ -# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j -# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B -# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 -# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 -# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I -# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVWzCCFVcCAQEwgZUwfjELMAkG -# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx -# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z -# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAd9r8C6Sp0q00AAAAAAB3zAN -# BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor -# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgRjg7DcI6 -# uhYfXWwAQ6hK0mPW7iyr2tzHR0DHSDJkscIwQgYKKwYBBAGCNwIBDDE0MDKgFIAS -# AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN -# BgkqhkiG9w0BAQEFAASCAQCt+qfRFudb8jpGDEFmJnD3uqC44a6SmTO+nFx0s2tf -# o+OTFNnRhwUMuTz/KVCfSwTzD/p2mD6JmuOuNmtTKyanjVNbYvwzO7LWedJh3d+T -# WEKZ6KYFbm6rPM+9DIINZNuK5Vb15vvNBUYI4PgFrSLGXdmRIB5xGiLRYWM/UET/ -# Sb4T6edTQKYx4vkDX9UcM4cYCx1u59hR6FgdCCHzU9/ZHYqN0AhBrHrTWGuqxx3E -# Oo0wdYJMRLH8zPFbzRNcG4qVlq95yDtWqzNcYMWybejIZenDg6am3ZldQFMoGU38 -# 76WP/a5unw8DKpkL/4ZO686G9Boh5Jc6U8mMGlLctW43oYIS5TCCEuEGCisGAQQB -# gjcDAwExghLRMIISzQYJKoZIhvcNAQcCoIISvjCCEroCAQMxDzANBglghkgBZQME -# AgEFADCCAVEGCyqGSIb3DQEJEAEEoIIBQASCATwwggE4AgEBBgorBgEEAYRZCgMB -# MDEwDQYJYIZIAWUDBAIBBQAEIITxzR9P1o4UBFnvUGa4yCqvmQhov1ZeA/XM1qBB -# 5/5xAgZgYx0i3j4YEzIwMjEwNDEwMTMxMTA2LjY0NlowBIACAfSggdCkgc0wgcox -# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt -# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1p -# Y3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJjAkBgNVBAsTHVRoYWxlcyBUU1Mg -# RVNOOjQ5QkMtRTM3QS0yMzNDMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFt -# cCBTZXJ2aWNloIIOPDCCBPEwggPZoAMCAQICEzMAAAFJgAhKuwmgMwsAAAAAAUkw -# DQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0 -# b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh -# dGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcN -# MjAxMTEyMTgyNTU3WhcNMjIwMjExMTgyNTU3WjCByjELMAkGA1UEBhMCVVMxEzAR -# BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p -# Y3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2Eg -# T3BlcmF0aW9uczEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046NDlCQy1FMzdBLTIz -# M0MxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggEiMA0G -# CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvE/uJD4XYdtp6OSoZPkolG9p3CWcw -# Lle1XkQMluEejNzIQMeWMsd8ZbujdfjJfWG/c3SOmZBwUAWEfVSdlCaMayt8gQHk -# KFikoD/bY1Q4y7Rfda7sCJw8CXf5wfLqzsGMvKkhtBFGOhqN/YqQm5j7B0c9qq12 -# 8i40lrrspOm31Vel+UAqlVt1L7Jb5MGKMWmEaoQpgvLGQq9NPBDMdgVjm1XwFFVc -# peBRWWn3Vb0UCWA6tqRuFLLaOsheYCA/jw6zw3+UwITm3JmnQVMIr9HALgvKY2uS -# 7lnSKiEaKRjb1oB1v0U0s8WPzkgbVpsyro+Uml2v7VreagzQzwvR+dWtAgMBAAGj -# ggEbMIIBFzAdBgNVHQ4EFgQUVnea8aPvuLS8NTXWT8mpc+pvJIEwHwYDVR0jBBgw -# FoAU1WM6XIoxkPNDe3xGG8UzaFqFbVUwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDov -# L2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljVGltU3RhUENB -# XzIwMTAtMDctMDEuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0 -# cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNUaW1TdGFQQ0FfMjAx -# MC0wNy0wMS5jcnQwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDCDAN -# BgkqhkiG9w0BAQsFAAOCAQEAEN54Cz4g7OBKqc8iwqLzNdQj2OCTxKmH+jr3Ayp+ -# AY/1qw4d77A/4WCP8g8PdToYiC47UXC6Fd2epJ07Olen50f88rFAz49H5BV7XlwP -# jiyE1ZU0vLKHiCcB2mibalui7W0dtg4W4bIqi7UlQkhBLERS5nn+zHYQg/rFQUQv -# vJrKpx2NM0MFgv2hki4B3JkDUfFwoHxYbAAJR1UtXaH+0PG1BW5yL1DLs451q7D/ -# RsHGmvx1M6+RKSr3qCUicbfQEa8vaP+nKJ0T/Da5vSqpSKocfD8dwM3Unn0tpoC+ -# lKmqQMDbllghGs7NVhps+9xG95s7beCMr3AuUZG/E6RQaTCCBnEwggRZoAMCAQIC -# CmEJgSoAAAAAAAIwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYD -# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy -# b3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRp -# ZmljYXRlIEF1dGhvcml0eSAyMDEwMB4XDTEwMDcwMTIxMzY1NVoXDTI1MDcwMTIx -# NDY1NVowfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV -# BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQG -# A1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggEiMA0GCSqGSIb3 -# DQEBAQUAA4IBDwAwggEKAoIBAQCpHQ28dxGKOiDs/BOX9fp/aZRrdFQQ1aUKAIKF -# ++18aEssX8XD5WHCdrc+Zitb8BVTJwQxH0EbGpUdzgkTjnxhMFmxMEQP8WCIhFRD -# DNdNuDgIs0Ldk6zWczBXJoKjRQ3Q6vVHgc2/JGAyWGBG8lhHhjKEHnRhZ5FfgVSx -# z5NMksHEpl3RYRNuKMYa+YaAu99h/EbBJx0kZxJyGiGKr0tkiVBisV39dx898Fd1 -# rL2KQk1AUdEPnAY+Z3/1ZsADlkR+79BL/W7lmsqxqPJ6Kgox8NpOBpG2iAg16Hgc -# sOmZzTznL0S6p/TcZL2kAcEgCZN4zfy8wMlEXV4WnAEFTyJNAgMBAAGjggHmMIIB -# 4jAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU1WM6XIoxkPNDe3xGG8UzaFqF -# bVUwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud -# EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQwVgYD -# VR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwv -# cHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEB -# BE4wTDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9j -# ZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwgaAGA1UdIAEB/wSBlTCB -# kjCBjwYJKwYBBAGCNy4DMIGBMD0GCCsGAQUFBwIBFjFodHRwOi8vd3d3Lm1pY3Jv -# c29mdC5jb20vUEtJL2RvY3MvQ1BTL2RlZmF1bHQuaHRtMEAGCCsGAQUFBwICMDQe -# MiAdAEwAZQBnAGEAbABfAFAAbwBsAGkAYwB5AF8AUwB0AGEAdABlAG0AZQBuAHQA -# LiAdMA0GCSqGSIb3DQEBCwUAA4ICAQAH5ohRDeLG4Jg/gXEDPZ2joSFvs+umzPUx -# vs8F4qn++ldtGTCzwsVmyWrf9efweL3HqJ4l4/m87WtUVwgrUYJEEvu5U4zM9GAS -# inbMQEBBm9xcF/9c+V4XNZgkVkt070IQyK+/f8Z/8jd9Wj8c8pl5SpFSAK84Dxf1 -# L3mBZdmptWvkx872ynoAb0swRCQiPM/tA6WWj1kpvLb9BOFwnzJKJ/1Vry/+tuWO -# M7tiX5rbV0Dp8c6ZZpCM/2pif93FSguRJuI57BlKcWOdeyFtw5yjojz6f32WapB4 -# pm3S4Zz5Hfw42JT0xqUKloakvZ4argRCg7i1gJsiOCC1JeVk7Pf0v35jWSUPei45 -# V3aicaoGig+JFrphpxHLmtgOR5qAxdDNp9DvfYPw4TtxCd9ddJgiCGHasFAeb73x -# 4QDf5zEHpJM692VHeOj4qEir995yfmFrb3epgcunCaw5u+zGy9iCtHLNHfS4hQEe -# gPsbiSpUObJb2sgNVZl6h3M7COaYLeqN4DMuEin1wC9UJyH3yKxO2ii4sanblrKn -# QqLJzxlBTeCG+SqaoxFmMNO7dDJL32N79ZmKLxvHIa9Zta7cRDyXUHHXodLFVeNp -# 3lfB0d4wwP3M5k37Db9dT+mdHhk4L7zPWAUu7w2gUDXa7wknHNWzfjUeCLraNtvT -# X4/edIhJEqGCAs4wggI3AgEBMIH4oYHQpIHNMIHKMQswCQYDVQQGEwJVUzETMBEG -# A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj -# cm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBP -# cGVyYXRpb25zMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo0OUJDLUUzN0EtMjMz -# QzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcG -# BSsOAwIaAxUAP+Wxrucu9GSImwAdD52BRGupqHeggYMwgYCkfjB8MQswCQYDVQQG -# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG -# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQg -# VGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIFAOQcG74wIhgPMjAy -# MTA0MTAyMDQyMzhaGA8yMDIxMDQxMTIwNDIzOFowdzA9BgorBgEEAYRZCgQBMS8w -# LTAKAgUA5BwbvgIBADAKAgEAAgIPsAIB/zAHAgEAAgIRdTAKAgUA5B1tPgIBADA2 -# BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIB -# AAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAJwLbdjnOrra0/+oVKDk+IXZYIN+JlqL -# jH1GeJ3tJPjay4PUX8sJqnah20MlfL6zrGOqez3wuc2i9qzioNZ52kx7Uqkv6pDa -# kDhbFgbtf98hUgICQkgi0enqTVch6y8ISJXCMW/IMQQCzzVofx981+uEM6FnpAVT -# XyrTutqA3UR8MYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT -# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m -# dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB -# IDIwMTACEzMAAAFJgAhKuwmgMwsAAAAAAUkwDQYJYIZIAWUDBAIBBQCgggFKMBoG -# CSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQgHZiBzaGB -# vCTz2seRfD/DtApg/rxFmMsMfJO6dSTYnOcwgfoGCyqGSIb3DQEJEAIvMYHqMIHn -# MIHkMIG9BCAolfr8WH1478zdhngQdSqc7DQL0sZx0OXG9a0fueihsjCBmDCBgKR+ -# MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS -# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMT -# HU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABSYAISrsJoDMLAAAA -# AAFJMCIEIFbD7x8R02d+iNVJ4YDrusUIiwA2AkOb4OsLJDlaZLFbMA0GCSqGSIb3 -# DQEBCwUABIIBAE4fGoU4p0DgXtqvMSINn22Ka3ld3o2vHaJG0Rgsh188chLikh+Y -# 3+/IIUKpgHIJ7K+++Y3fxP7KDX4Zmr/ElhPYsbQ7N7FCw1Rs4kNC3g9Pfz339m4e -# DOHUTMb0ltdNHRc34O9g88pqwMNLLNiQNrubnLtW55H4TtVW2BvET+X7JYwE9I0q -# YWu7nBgOscnCm7265xMlFtzJJcti7tl0be8w5Zesx6+oWgGOfz53YI+38rYRT+aj -# pCOvBGL+5kMBk/kzfKZCybDofbJXKNaOr/VpxKVHsNfwqjVgI51MqNBshXBk4/po -# ycMvQ6irTSr7OMn1e0okVfSFCKEJqLsbYwY= -# SIG # End signature block diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Package/tools/uninstall.ps1 b/src/Lucene.Net.CodeAnalysis.Dev.Package/tools/uninstall.ps1 deleted file mode 100644 index 6f54d91..0000000 --- a/src/Lucene.Net.CodeAnalysis.Dev.Package/tools/uninstall.ps1 +++ /dev/null @@ -1,258 +0,0 @@ -param($installPath, $toolsPath, $package, $project) - -if($project.Object.SupportsPackageDependencyResolution) -{ - if($project.Object.SupportsPackageDependencyResolution()) - { - # Do not uninstall analyzers via uninstall.ps1, instead let the project system handle it. - return - } -} - -$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve - -foreach($analyzersPath in $analyzersPaths) -{ - # Uninstall the language agnostic analyzers. - if (Test-Path $analyzersPath) - { - foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll) - { - if($project.Object.AnalyzerReferences) - { - $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName) - } - } - } -} - -# $project.Type gives the language name like (C# or VB.NET) -$languageFolder = "" -if($project.Type -eq "C#") -{ - $languageFolder = "cs" -} -if($project.Type -eq "VB.NET") -{ - $languageFolder = "vb" -} -if($languageFolder -eq "") -{ - return -} - -foreach($analyzersPath in $analyzersPaths) -{ - # Uninstall language specific analyzers. - $languageAnalyzersPath = join-path $analyzersPath $languageFolder - if (Test-Path $languageAnalyzersPath) - { - foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll) - { - if($project.Object.AnalyzerReferences) - { - try - { - $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName) - } - catch - { - - } - } - } - } -} -# SIG # Begin signature block -# MIIjnwYJKoZIhvcNAQcCoIIjkDCCI4wCAQExDzANBglghkgBZQMEAgEFADB5Bgor -# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG -# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDC68wb97fg0QGL -# yXrxJhYfmibzcOh8caqC0uZprfczDaCCDYEwggX/MIID56ADAgECAhMzAAAB32vw -# LpKnSrTQAAAAAAHfMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD -# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy -# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p -# bmcgUENBIDIwMTEwHhcNMjAxMjE1MjEzMTQ1WhcNMjExMjAyMjEzMTQ1WjB0MQsw -# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u -# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy -# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB -# AQC2uxlZEACjqfHkuFyoCwfL25ofI9DZWKt4wEj3JBQ48GPt1UsDv834CcoUUPMn -# s/6CtPoaQ4Thy/kbOOg/zJAnrJeiMQqRe2Lsdb/NSI2gXXX9lad1/yPUDOXo4GNw -# PjXq1JZi+HZV91bUr6ZjzePj1g+bepsqd/HC1XScj0fT3aAxLRykJSzExEBmU9eS -# yuOwUuq+CriudQtWGMdJU650v/KmzfM46Y6lo/MCnnpvz3zEL7PMdUdwqj/nYhGG -# 3UVILxX7tAdMbz7LN+6WOIpT1A41rwaoOVnv+8Ua94HwhjZmu1S73yeV7RZZNxoh -# EegJi9YYssXa7UZUUkCCA+KnAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE -# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUOPbML8IdkNGtCfMmVPtvI6VZ8+Mw -# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 -# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDYzMDA5MB8GA1UdIwQYMBaAFEhu -# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu -# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w -# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 -# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx -# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAnnqH -# tDyYUFaVAkvAK0eqq6nhoL95SZQu3RnpZ7tdQ89QR3++7A+4hrr7V4xxmkB5BObS -# 0YK+MALE02atjwWgPdpYQ68WdLGroJZHkbZdgERG+7tETFl3aKF4KpoSaGOskZXp -# TPnCaMo2PXoAMVMGpsQEQswimZq3IQ3nRQfBlJ0PoMMcN/+Pks8ZTL1BoPYsJpok -# t6cql59q6CypZYIwgyJ892HpttybHKg1ZtQLUlSXccRMlugPgEcNZJagPEgPYni4 -# b11snjRAgf0dyQ0zI9aLXqTxWUU5pCIFiPT0b2wsxzRqCtyGqpkGM8P9GazO8eao -# mVItCYBcJSByBx/pS0cSYwBBHAZxJODUqxSXoSGDvmTfqUJXntnWkL4okok1FiCD -# Z4jpyXOQunb6egIXvkgQ7jb2uO26Ow0m8RwleDvhOMrnHsupiOPbozKroSa6paFt -# VSh89abUSooR8QdZciemmoFhcWkEwFg4spzvYNP4nIs193261WyTaRMZoceGun7G -# CT2Rl653uUj+F+g94c63AhzSq4khdL4HlFIP2ePv29smfUnHtGq6yYFDLnT0q/Y+ -# Di3jwloF8EWkkHRtSuXlFUbTmwr/lDDgbpZiKhLS7CBTDj32I0L5i532+uHczw82 -# oZDmYmYmIUSMbZOgS65h797rj5JJ6OkeEUJoAVwwggd6MIIFYqADAgECAgphDpDS -# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK -# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 -# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 -# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla -# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS -# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT -# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB -# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG -# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S -# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz -# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 -# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u -# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 -# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl -# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP -# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB -# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF -# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM -# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ -# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud -# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO -# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 -# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y -# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p -# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y -# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB -# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw -# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA -# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY -# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj -# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd -# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ -# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf -# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ -# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j -# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B -# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 -# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 -# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I -# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVdDCCFXACAQEwgZUwfjELMAkG -# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx -# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z -# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAd9r8C6Sp0q00AAAAAAB3zAN -# BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor -# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgF1ypFyzl -# AvvWGVCeXczrfpXmJNm9vpyjcwd4y4ivfqowQgYKKwYBBAGCNwIBDDE0MDKgFIAS -# AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN -# BgkqhkiG9w0BAQEFAASCAQBtnC8PmVgmkVoKR15F39ljf7fpP29Vlo9dnBmOcDAw -# lHnt37zK9UIQSoyqiMhY/lt8iltz49NEj3D3ddXTXdIOXlfjUYvoIIrNUahgDF/Z -# 3iQVhBW6Me8FEF4ImSntGkkvf86Hp5dlLCrpdDDWVVJkCxRGvCFXC0aNPHlFHRF1 -# Hbqqstm9xbYliNTk3BhJoo56j8XO61JkNEjzva3gemuG4dVhFSz9OF5HsjPTpTiJ -# pg5//GDE5xeho5kwxk8Algyfac3vseJXLr6388cIP556sruynQumo0+K0cxyxhVI -# cyvYlZAi4WzRpNNnWP8VXFb0ITFbgr0SLBIYUrQGFr2QoYIS/jCCEvoGCisGAQQB -# gjcDAwExghLqMIIS5gYJKoZIhvcNAQcCoIIS1zCCEtMCAQMxDzANBglghkgBZQME -# AgEFADCCAVkGCyqGSIb3DQEJEAEEoIIBSASCAUQwggFAAgEBBgorBgEEAYRZCgMB -# MDEwDQYJYIZIAWUDBAIBBQAEIJp8zyESzF9BGcJWXqSm1vrCJ/LFDEjr5Yc3y0OW -# 46OzAgZgY0sEcZMYEzIwMjEwNDEwMTMxMTI2LjQ3OFowBIACAfSggdikgdUwgdIx -# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt -# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1p -# Y3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEmMCQGA1UECxMdVGhh -# bGVzIFRTUyBFU046RTA0MS00QkVFLUZBN0UxJTAjBgNVBAMTHE1pY3Jvc29mdCBU -# aW1lLVN0YW1wIFNlcnZpY2Wggg5NMIIE+TCCA+GgAwIBAgITMwAAATdBj0PnWltv -# pwAAAAABNzANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMK -# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 -# IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0Eg -# MjAxMDAeFw0yMDEwMTUxNzI4MTRaFw0yMjAxMTIxNzI4MTRaMIHSMQswCQYDVQQG -# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG -# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQg -# SXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1Mg -# RVNOOkUwNDEtNEJFRS1GQTdFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFt -# cCBTZXJ2aWNlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxBHuadEl -# m3G5tikhTzjSDB0+9sXmUhUyDVRj0Y4vz9rZ9sykNobL5/6At5zOkeB2bl9IXvVd -# yS/ZJNZT373knzrQ347z30Mmw7++VU/CE+4x4w9kb5bqQHfSzbJQt6KmWsuMmJLz -# g4R5MeJs5MY5YdPLxoMoDRcTi//KoMFR0KzS1/324D2/4KkHD1Xt+s0xY0DICUOK -# 1RbmJCKEgBP1/GDZjuZQBS9Di89yTnvLJV+Lr1QtriH4EqmRoAdmV3zJ0GJsr5vh -# GPmKfOPCRSk7Q8igX7goFnCLzpYcfHGCqoR/mw95gfQpwymVwxZB0PkGMrQw+LKV -# Pa/FHP4C4KO+QQIDAQABo4IBGzCCARcwHQYDVR0OBBYEFA1gsHMM+udgY7rEne66 -# OyzxlE9lMB8GA1UdIwQYMBaAFNVjOlyKMZDzQ3t8RhvFM2hahW1VMFYGA1UdHwRP -# ME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1 -# Y3RzL01pY1RpbVN0YVBDQV8yMDEwLTA3LTAxLmNybDBaBggrBgEFBQcBAQROMEww -# SgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMv -# TWljVGltU3RhUENBXzIwMTAtMDctMDEuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0l -# BAwwCgYIKwYBBQUHAwgwDQYJKoZIhvcNAQELBQADggEBAJ32U9d90RVuAUb9NsnX -# BG1K42qjhU+jHvwBdbipIcX4Wg7dH5ZduQZj3gWgKADZ5z+TehX7GnBbi265VI7x -# DRsFe2CjkTm4JIoisdKwYBDruS+YRRBG4B1ERuWi54XGwx+lSA+iQNrIi6Jm0CL/ -# MfQLvwsqPJSGP69OEHCyaExos486+X3JTuGV11CBl/BO7r8UHbx/rE6fZrlZZYab -# IF6aeahvTL14LvZLV/bMzYSODsbjHHsTm9QaGm1ijhagCdbkAqr8+7HAgYEar8XP -# lzxUhVI4ShVB5ZGd9gZ2yBkwxdA0oFc745TdOPrbP79vd0ePqgvJDH5tkOhTRNI5 -# 5XQwggZxMIIEWaADAgECAgphCYEqAAAAAAACMA0GCSqGSIb3DQEBCwUAMIGIMQsw -# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u -# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNy -# b3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0xMDA3MDEy -# MTM2NTVaFw0yNTA3MDEyMTQ2NTVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX -# YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg -# Q29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAy -# MDEwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqR0NvHcRijog7PwT -# l/X6f2mUa3RUENWlCgCChfvtfGhLLF/Fw+Vhwna3PmYrW/AVUycEMR9BGxqVHc4J -# E458YTBZsTBED/FgiIRUQwzXTbg4CLNC3ZOs1nMwVyaCo0UN0Or1R4HNvyRgMlhg -# RvJYR4YyhB50YWeRX4FUsc+TTJLBxKZd0WETbijGGvmGgLvfYfxGwScdJGcSchoh -# iq9LZIlQYrFd/XcfPfBXday9ikJNQFHRD5wGPmd/9WbAA5ZEfu/QS/1u5ZrKsajy -# eioKMfDaTgaRtogINeh4HLDpmc085y9Euqf03GS9pAHBIAmTeM38vMDJRF1eFpwB -# BU8iTQIDAQABo4IB5jCCAeIwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFNVj -# OlyKMZDzQ3t8RhvFM2hahW1VMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsG -# A1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJc -# YmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9z -# b2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIz -# LmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWlj -# cm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0 -# MIGgBgNVHSABAf8EgZUwgZIwgY8GCSsGAQQBgjcuAzCBgTA9BggrBgEFBQcCARYx -# aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL1BLSS9kb2NzL0NQUy9kZWZhdWx0Lmh0 -# bTBABggrBgEFBQcCAjA0HjIgHQBMAGUAZwBhAGwAXwBQAG8AbABpAGMAeQBfAFMA -# dABhAHQAZQBtAGUAbgB0AC4gHTANBgkqhkiG9w0BAQsFAAOCAgEAB+aIUQ3ixuCY -# P4FxAz2do6Ehb7Prpsz1Mb7PBeKp/vpXbRkws8LFZslq3/Xn8Hi9x6ieJeP5vO1r -# VFcIK1GCRBL7uVOMzPRgEop2zEBAQZvcXBf/XPleFzWYJFZLdO9CEMivv3/Gf/I3 -# fVo/HPKZeUqRUgCvOA8X9S95gWXZqbVr5MfO9sp6AG9LMEQkIjzP7QOllo9ZKby2 -# /QThcJ8ySif9Va8v/rbljjO7Yl+a21dA6fHOmWaQjP9qYn/dxUoLkSbiOewZSnFj -# nXshbcOco6I8+n99lmqQeKZt0uGc+R38ONiU9MalCpaGpL2eGq4EQoO4tYCbIjgg -# tSXlZOz39L9+Y1klD3ouOVd2onGqBooPiRa6YacRy5rYDkeagMXQzafQ732D8OE7 -# cQnfXXSYIghh2rBQHm+98eEA3+cxB6STOvdlR3jo+KhIq/fecn5ha293qYHLpwms -# ObvsxsvYgrRyzR30uIUBHoD7G4kqVDmyW9rIDVWZeodzOwjmmC3qjeAzLhIp9cAv -# VCch98isTtoouLGp25ayp0Kiyc8ZQU3ghvkqmqMRZjDTu3QyS99je/WZii8bxyGv -# WbWu3EQ8l1Bx16HSxVXjad5XwdHeMMD9zOZN+w2/XU/pnR4ZOC+8z1gFLu8NoFA1 -# 2u8JJxzVs341Hgi62jbb01+P3nSISRKhggLXMIICQAIBATCCAQChgdikgdUwgdIx -# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt -# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1p -# Y3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEmMCQGA1UECxMdVGhh -# bGVzIFRTUyBFU046RTA0MS00QkVFLUZBN0UxJTAjBgNVBAMTHE1pY3Jvc29mdCBU -# aW1lLVN0YW1wIFNlcnZpY2WiIwoBATAHBgUrDgMCGgMVAOq7qDk4iVz8ITuZbUFr -# AG7ecxqcoIGDMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0 -# b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh -# dGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwDQYJ -# KoZIhvcNAQEFBQACBQDkG6DlMCIYDzIwMjEwNDEwMTE1ODI5WhgPMjAyMTA0MTEx -# MTU4MjlaMHcwPQYKKwYBBAGEWQoEATEvMC0wCgIFAOQboOUCAQAwCgIBAAICHm0C -# Af8wBwIBAAICEXswCgIFAOQc8mUCAQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYB -# BAGEWQoDAqAKMAgCAQACAwehIKEKMAgCAQACAwGGoDANBgkqhkiG9w0BAQUFAAOB -# gQBfiUj3omUNFpF9HxVTkkETxKTdGKdBxFT7khTs1YiYGMaE/t7w9+1vMDqX1/mD -# wTJU1WDfZFfisiECBWWXggA96sgEn6HbFy2xTKdusLUKJtJ2GZccZ7vX7MsUmiIt -# Lv0MV1merLbSQGNQMPh9aj3AAIyGjfS0HpLy6rhGTjK3PzGCAw0wggMJAgEBMIGT -# MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS -# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMT -# HU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABN0GPQ+daW2+nAAAA -# AAE3MA0GCWCGSAFlAwQCAQUAoIIBSjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQ -# AQQwLwYJKoZIhvcNAQkEMSIEIAluvxzIzodFrlM64yKjGIBV2DYZ/YRbVIA0BxpL -# QIOUMIH6BgsqhkiG9w0BCRACLzGB6jCB5zCB5DCBvQQgHVl+r8CeBJ0iyX/aGZD2 -# YbQ7gk+U7N7BQiTDKAYSHBAwgZgwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UE -# CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z -# b2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQ -# Q0EgMjAxMAITMwAAATdBj0PnWltvpwAAAAABNzAiBCCZZFZnxWGFC5arcI4nCqWo -# I0x05qEi5RyHPlqc5jw3ADANBgkqhkiG9w0BAQsFAASCAQADPkURIFZzFg2kSpcV -# oxedp9KyVZc8X6tMSXoK9hrezse5Zt0sNtnunNQXJIuiX+N3AhQRahRP2Jll3QHg -# 0trfVkh58bp0tKJwMpbhiEjksPPbN0mjkfilaYfHX6W5CaUubMYD8wHfws5IPMEp -# hnyKv1ouoclGPVpXONmevND5kJ+xJ4xeno57JaOqirM8LbvduiTRmlU8inyjxzSC -# fPM1AN2c5x8tkxJkKYWupwgO4sYl5xTuvZYhpFS4nJnFkdIfiGeK+tzbQP3uFAkm -# /9s54qbJ366ZVuSvN6Cx8OZsCOaD0oYrTn9a8SgZH89LMtrMqUtmof9E1YEpFc4o -# OFpN -# SIG # End signature block diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Sample/Lucene.Net.CodeAnalysis.Dev.Sample.csproj b/src/Lucene.Net.CodeAnalysis.Dev.Sample/Lucene.Net.CodeAnalysis.Dev.Sample.csproj new file mode 100644 index 0000000..7ac3045 --- /dev/null +++ b/src/Lucene.Net.CodeAnalysis.Dev.Sample/Lucene.Net.CodeAnalysis.Dev.Sample.csproj @@ -0,0 +1,114 @@ + + + + + + + net8.0 + enable + enable + + + + <_ArtifactsDirectory>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\_artifacts')) + <_NuGetPackageOutputPath>$(_ArtifactsDirectory)\NuGetPackages\$(Configuration) + <_PackageVersionPropsFilePath>$(_NuGetPackageOutputPath)\Lucene.Net.CodeAnalysis.Dev.Version.props + + obj\LocalNuGetPackages + <_RestorePackagesPath>$(RestorePackagesPath)\lucene.net.codeanalsis.dev + + + + $(RestoreSources);$(_NuGetPackageOutputPath) + + + + $(RestoreSources);https://api.nuget.org/v3/index.json + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1000Sample.cs b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1000Sample.cs new file mode 100644 index 0000000..7cbdda5 --- /dev/null +++ b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1000Sample.cs @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +namespace Lucene.Net.CodeAnalysis.Dev.Sample; + +public class LuceneDev1000Sample +{ + protected internal bool LessThan(float termA, float termB) + { + return termA < termB; + } +} diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1001Sample.cs b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1001Sample.cs new file mode 100644 index 0000000..8a0978f --- /dev/null +++ b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1001Sample.cs @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +using System.Globalization; + +namespace Lucene.Net.CodeAnalysis.Dev.Sample; + +public class LuceneDev1001Sample +{ + private readonly float float1 = 1f; + + public void MyMethod() + { + string result = float1.ToString(CultureInfo.InvariantCulture); + } +} diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1002Sample.cs b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1002Sample.cs new file mode 100644 index 0000000..e5bd594 --- /dev/null +++ b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1002Sample.cs @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +namespace Lucene.Net.CodeAnalysis.Dev.Sample; + +public class LuceneDev1002Sample +{ + private readonly float float1 = 1f; + private readonly float float2 = 3.14f; + + public void MyMethod() + { + long foo = 33; + var result = ((double)float1 * (double)float2) / foo; + } +} diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1003Sample.cs b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1003Sample.cs new file mode 100644 index 0000000..187fbdd --- /dev/null +++ b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1003Sample.cs @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +namespace Lucene.Net.CodeAnalysis.Dev.Sample; + +public class LuceneDev1003Sample +{ + public static bool ParseChar(string id, int[] pos, char ch) + { + int start = pos[0]; + //pos[0] = PatternProps.SkipWhiteSpace(id, pos[0]); + if (pos[0] == id.Length || + id[pos[0]] != ch) + { + pos[0] = start; + return false; + } + ++pos[0]; + return true; + } +} diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1004Sample.cs b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1004Sample.cs new file mode 100644 index 0000000..8b8d1c2 --- /dev/null +++ b/src/Lucene.Net.CodeAnalysis.Dev.Sample/LuceneDev1004Sample.cs @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +namespace Lucene.Net.CodeAnalysis.Dev.Sample; + +public class LuceneDev1004Sample +{ + public static byte[] GetVersionByteArrayFromCompactInt32(int version) // ICU4N specific - Renamed from GetVersionByteArrayFromCompactInt + { + return new byte[] { + (byte)(version >> 24), + (byte)(version >> 16), + (byte)(version >> 8), + (byte)(version) + }; + } +} diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Vsix/Lucene.Net.CodeAnalysis.Dev.Vsix.csproj b/src/Lucene.Net.CodeAnalysis.Dev.Vsix/Lucene.Net.CodeAnalysis.Dev.Vsix.csproj index dc023da..cc5cc81 100644 --- a/src/Lucene.Net.CodeAnalysis.Dev.Vsix/Lucene.Net.CodeAnalysis.Dev.Vsix.csproj +++ b/src/Lucene.Net.CodeAnalysis.Dev.Vsix/Lucene.Net.CodeAnalysis.Dev.Vsix.csproj @@ -1,14 +1,38 @@ - - - + + + + net472 Lucene.Net.CodeAnalysis.Dev.Vsix Lucene.Net.CodeAnalysis.Dev.Vsix + + + false - + + false false false @@ -16,24 +40,25 @@ false false Roslyn + + + $(VsSDKInstall)\Microsoft.VsSDK.targets - - + + + + Program $(DevEnvDir)devenv.exe /rootsuffix $(VSSDKTargetPlatformRegRootSuffix) - - - - - + @@ -45,4 +70,4 @@ - \ No newline at end of file + diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Vsix/localized.resources.targets b/src/Lucene.Net.CodeAnalysis.Dev.Vsix/localized.resources.targets new file mode 100644 index 0000000..6ca9c6d --- /dev/null +++ b/src/Lucene.Net.CodeAnalysis.Dev.Vsix/localized.resources.targets @@ -0,0 +1,42 @@ + + + + + $(GetVsixSourceItemsDependsOn);IncludeResourcesInVsix + + + + + + %(RecursiveDir) + + Microsoft.VisualStudio.Analyzer + + + %(RecursiveDir) + + Microsoft.VisualStudio.Analyzer + + + + + diff --git a/src/Lucene.Net.CodeAnalysis.Dev.Vsix/source.extension.vsixmanifest b/src/Lucene.Net.CodeAnalysis.Dev.Vsix/source.extension.vsixmanifest index 8f7cdae..4ae95c7 100644 --- a/src/Lucene.Net.CodeAnalysis.Dev.Vsix/source.extension.vsixmanifest +++ b/src/Lucene.Net.CodeAnalysis.Dev.Vsix/source.extension.vsixmanifest @@ -1,26 +1,26 @@ - + - + Lucene.Net.CodeAnalysis.Dev - Analyzers to help with the development of the Lucene.NET port from Java to C#. + This is a debugging project for Lucene.Net.CodeAnalysis.Dev analyzers and code fixes. - - - + + amd64 + - + - + + - - + + - \ No newline at end of file + diff --git a/src/Lucene.Net.CodeAnalysis.Dev/AnalyzerReleases.Shipped.md b/src/Lucene.Net.CodeAnalysis.Dev/AnalyzerReleases.Shipped.md new file mode 100644 index 0000000..f6b0d6c --- /dev/null +++ b/src/Lucene.Net.CodeAnalysis.Dev/AnalyzerReleases.Shipped.md @@ -0,0 +1,6 @@ +## Release 1.0 + +### New Rules + + Rule ID | Category | Severity | Notes +---------|----------|----------|------- diff --git a/src/Lucene.Net.CodeAnalysis.Dev/AnalyzerReleases.Unshipped.md b/src/Lucene.Net.CodeAnalysis.Dev/AnalyzerReleases.Unshipped.md new file mode 100644 index 0000000..b007489 --- /dev/null +++ b/src/Lucene.Net.CodeAnalysis.Dev/AnalyzerReleases.Unshipped.md @@ -0,0 +1,9 @@ +### New Rules + + Rule ID | Category | Severity | Notes +---------------|----------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------- + LuceneDev1000 | Design | Warning | Floating point types should not be compared for exact equality + LuceneDev1001 | Design | Warning | Floating point types should be formatted with J2N methods + LuceneDev1002 | Design | Warning | Floating point type arithmetic needs to be checked + LuceneDev1003 | Design | Warning | Method parameters that accept array types should be analyzed to determine whether they are better suited to be ref or out parameters + LuceneDev1004 | Design | Warning | Methods that return array types should be analyzed to determine whether they are better suited to be one or more out parameters or to return a ValueTuple diff --git a/src/Lucene.Net.CodeAnalysis.Dev/Lucene.Net.CodeAnalysis.Dev.csproj b/src/Lucene.Net.CodeAnalysis.Dev/Lucene.Net.CodeAnalysis.Dev.csproj index 649e37b..eb4a28b 100644 --- a/src/Lucene.Net.CodeAnalysis.Dev/Lucene.Net.CodeAnalysis.Dev.csproj +++ b/src/Lucene.Net.CodeAnalysis.Dev/Lucene.Net.CodeAnalysis.Dev.csproj @@ -1,23 +1,79 @@ + + + netstandard2.0 - false + enable + + + false + + true + true + - 1.0.2 - *$(MSBuildProjectFile)* + + true + true + + + + PrepareResources;$(CompileDependsOn) + + + true + 1.0.0 + 1.0.* + + + + - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + - - + + + MSBuild:Compile + $(IntermediateOutputPath)/Resources.designer.cs + CSharp + Lucene.Net.CodeAnalysis.Dev + Resources + diff --git a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1001_FloatingPointFormattingCSCodeAnalyzer.cs b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1001_FloatingPointFormattingCSCodeAnalyzer.cs deleted file mode 100644 index 88a7010..0000000 --- a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1001_FloatingPointFormattingCSCodeAnalyzer.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Lucene.Net.CodeAnalysis.Dev.Helpers; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; - -namespace Lucene.Net.CodeAnalysis.Dev -{ - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public class LuceneDev1001_FloatingPointFormattingCSCodeAnalyzer : DiagnosticAnalyzer - { - public const string DiagnosticId = "LuceneDev1001"; - - // You can change these strings in the Resources.resx file. If you do not want your analyzer to be localize-able, you can use regular strings for Title and MessageFormat. - // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/Localizing%20Analyzers.md for more on localization - private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.LuceneDev1001_AnalyzerTitle), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.LuceneDev1001_AnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableString Description = new LocalizableResourceString(nameof(Resources.LuceneDev1001_AnalyzerDescription), Resources.ResourceManager, typeof(Resources)); - private const string Category = "Design"; - - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description); - - public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } } - - public override void Initialize(AnalysisContext context) - { - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze); - context.EnableConcurrentExecution(); - - // TODO: Consider registering other actions that act on syntax instead of or in addition to symbols - // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/Analyzer%20Actions%20Semantics.md for more information - //context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType); - context.RegisterSyntaxNodeAction(AnalyzeNodeCS, SyntaxKind.SimpleMemberAccessExpression); - //context.RegisterSyntaxNodeAction(AnalyzeEqualsMethodNode, SyntaxKind.InvocationExpression); - } - - private static void AnalyzeNodeCS(SyntaxNodeAnalysisContext context) - { - if (context.Node is Microsoft.CodeAnalysis.CSharp.Syntax.MemberAccessExpressionSyntax memberAccessExpression) - { - if (!(memberAccessExpression.Parent is Microsoft.CodeAnalysis.CSharp.Syntax.InvocationExpressionSyntax)) - return; - - if (memberAccessExpression.Name.Identifier.ValueText != "ToString") - return; - - var leftSymbolInfo = Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetSymbolInfo(context.SemanticModel, memberAccessExpression.Expression); - - if (!FloatingPoint.IsFloatingPointType(leftSymbolInfo)) - return; // Check passed - - context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation(), memberAccessExpression.ToString())); - } - } - } -} diff --git a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer.cs b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer.cs similarity index 59% rename from src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer.cs rename to src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer.cs index 1ddd54c..d70ae00 100644 --- a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer.cs +++ b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer.cs @@ -1,31 +1,35 @@ -using Lucene.Net.CodeAnalysis.Dev.Helpers; +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +using Lucene.Net.CodeAnalysis.Dev.Utility; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -using System; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; -using System.Threading; namespace Lucene.Net.CodeAnalysis.Dev { [DiagnosticAnalyzer(LanguageNames.CSharp)] public class LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer : DiagnosticAnalyzer { - public const string DiagnosticId = "LuceneDev1000"; - - // You can change these strings in the Resources.resx file. If you do not want your analyzer to be localize-able, you can use regular strings for Title and MessageFormat. - // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/Localizing%20Analyzers.md for more on localization - private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.LuceneDev1000_AnalyzerTitle), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.LuceneDev1000_AnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableString Description = new LocalizableResourceString(nameof(Resources.LuceneDev1000_AnalyzerDescription), Resources.ResourceManager, typeof(Resources)); - private const string Category = "Design"; - - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description); - - public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } } + public override ImmutableArray SupportedDiagnostics => [Descriptors.LuceneDev1000_FloatingPointEquality]; public override void Initialize(AnalysisContext context) { @@ -47,7 +51,7 @@ public override void Initialize(AnalysisContext context) private static void AnalyzeNodeCS(SyntaxNodeAnalysisContext context) { - if (context.Node is Microsoft.CodeAnalysis.CSharp.Syntax.BinaryExpressionSyntax binaryExpression) + if (context.Node is BinaryExpressionSyntax binaryExpression) { var leftSymbolInfo = Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetSymbolInfo(context.SemanticModel, binaryExpression.Left); var rightSymbolInfo = Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetSymbolInfo(context.SemanticModel, binaryExpression.Right); @@ -58,11 +62,11 @@ private static void AnalyzeNodeCS(SyntaxNodeAnalysisContext context) if (!FloatingPoint.IsFloatingPointType(leftSymbolInfo) && !FloatingPoint.IsFloatingPointType(rightSymbolInfo)) return; // Check passed - context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation(), binaryExpression.ToString())); + context.ReportDiagnostic(Diagnostic.Create(Descriptors.LuceneDev1000_FloatingPointEquality, context.Node.GetLocation(), binaryExpression.ToString())); } - else if (context.Node is Microsoft.CodeAnalysis.CSharp.Syntax.MemberAccessExpressionSyntax memberAccessExpression) + else if (context.Node is MemberAccessExpressionSyntax memberAccessExpression) { - if (!(memberAccessExpression.Parent is Microsoft.CodeAnalysis.CSharp.Syntax.InvocationExpressionSyntax)) + if (!(memberAccessExpression.Parent is InvocationExpressionSyntax)) return; if (memberAccessExpression.Name.Identifier.ValueText != "Equals") @@ -73,7 +77,7 @@ private static void AnalyzeNodeCS(SyntaxNodeAnalysisContext context) if (!FloatingPoint.IsFloatingPointType(leftSymbolInfo)) return; // Check passed - context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation(), memberAccessExpression.ToString())); + context.ReportDiagnostic(Diagnostic.Create(Descriptors.LuceneDev1000_FloatingPointEquality, context.Node.GetLocation(), memberAccessExpression.ToString())); } } } diff --git a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1001_FloatingPointFormattingCSCodeAnalyzer.cs b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1001_FloatingPointFormattingCSCodeAnalyzer.cs new file mode 100644 index 0000000..86f2af4 --- /dev/null +++ b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1001_FloatingPointFormattingCSCodeAnalyzer.cs @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +using Lucene.Net.CodeAnalysis.Dev.Utility; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Immutable; + +namespace Lucene.Net.CodeAnalysis.Dev +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class LuceneDev1001_FloatingPointFormattingCSCodeAnalyzer : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics => [Descriptors.LuceneDev1001_FloatingPointFormatting]; + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze); + context.EnableConcurrentExecution(); + + // TODO: Consider registering other actions that act on syntax instead of or in addition to symbols + // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/Analyzer%20Actions%20Semantics.md for more information + //context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType); + context.RegisterSyntaxNodeAction(AnalyzeNodeCS, SyntaxKind.SimpleMemberAccessExpression); + //context.RegisterSyntaxNodeAction(AnalyzeEqualsMethodNode, SyntaxKind.InvocationExpression); + } + + private static void AnalyzeNodeCS(SyntaxNodeAnalysisContext context) + { + if (context.Node is MemberAccessExpressionSyntax memberAccessExpression) + { + if (!(memberAccessExpression.Parent is InvocationExpressionSyntax)) + return; + + if (memberAccessExpression.Name.Identifier.ValueText != "ToString") + return; + + var leftSymbolInfo = Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetSymbolInfo(context.SemanticModel, memberAccessExpression.Expression); + + if (!FloatingPoint.IsFloatingPointType(leftSymbolInfo)) + return; // Check passed + + context.ReportDiagnostic(Diagnostic.Create(Descriptors.LuceneDev1001_FloatingPointFormatting, context.Node.GetLocation(), memberAccessExpression.ToString())); + } + } + } +} diff --git a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1002_FloatingPointArithmeticCSCodeAnalyzer.cs b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1002_FloatingPointArithmeticCSCodeAnalyzer.cs similarity index 62% rename from src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1002_FloatingPointArithmeticCSCodeAnalyzer.cs rename to src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1002_FloatingPointArithmeticCSCodeAnalyzer.cs index 31c2313..4b38638 100644 --- a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1002_FloatingPointArithmeticCSCodeAnalyzer.cs +++ b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1002_FloatingPointArithmeticCSCodeAnalyzer.cs @@ -1,31 +1,34 @@ -using Lucene.Net.CodeAnalysis.Dev.Helpers; +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +using Lucene.Net.CodeAnalysis.Dev.Utility; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -using System; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; -using System.Threading; namespace Lucene.Net.CodeAnalysis.Dev { [DiagnosticAnalyzer(LanguageNames.CSharp)] public class LuceneDev1002_FloatingPointArithmeticCSCodeAnalyzer : DiagnosticAnalyzer { - public const string DiagnosticId = "LuceneDev1002"; - - // You can change these strings in the Resources.resx file. If you do not want your analyzer to be localize-able, you can use regular strings for Title and MessageFormat. - // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/Localizing%20Analyzers.md for more on localization - private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.LuceneDev1002_AnalyzerTitle), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.LuceneDev1002_AnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableString Description = new LocalizableResourceString(nameof(Resources.LuceneDev1002_AnalyzerDescription), Resources.ResourceManager, typeof(Resources)); - private const string Category = "Design"; - - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description); - - public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } } + public override ImmutableArray SupportedDiagnostics => [Descriptors.LuceneDev1002_FloatingPointArithmetic]; public override void Initialize(AnalysisContext context) { @@ -70,7 +73,7 @@ private static void AnalyzeNodeCS(SyntaxNodeAnalysisContext context) // } - context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation(), context.Node.ToString())); + context.ReportDiagnostic(Diagnostic.Create(Descriptors.LuceneDev1002_FloatingPointArithmetic, context.Node.GetLocation(), context.Node.ToString())); } } } diff --git a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1003_ArrayMethodParameterCSCodeAnalyzer.cs b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1003_ArrayMethodParameterCSCodeAnalyzer.cs similarity index 57% rename from src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1003_ArrayMethodParameterCSCodeAnalyzer.cs rename to src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1003_ArrayMethodParameterCSCodeAnalyzer.cs index 56ef9af..5c4f6a0 100644 --- a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1003_ArrayMethodParameterCSCodeAnalyzer.cs +++ b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1003_ArrayMethodParameterCSCodeAnalyzer.cs @@ -1,4 +1,24 @@ -using Microsoft.CodeAnalysis; +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +using Lucene.Net.CodeAnalysis.Dev.Utility; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; @@ -9,18 +29,7 @@ namespace Lucene.Net.CodeAnalysis.Dev [DiagnosticAnalyzer(LanguageNames.CSharp)] public class LuceneDev1003_ArrayMethodParameterCSCodeAnalyzer : DiagnosticAnalyzer { - public const string DiagnosticId = "LuceneDev1003"; - - // You can change these strings in the Resources.resx file. If you do not want your analyzer to be localize-able, you can use regular strings for Title and MessageFormat. - // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/Localizing%20Analyzers.md for more on localization - private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.LuceneDev1003_AnalyzerTitle), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.LuceneDev1003_AnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableString Description = new LocalizableResourceString(nameof(Resources.LuceneDev1003_AnalyzerDescription), Resources.ResourceManager, typeof(Resources)); - private const string Category = "Design"; - - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description); - - public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } } + public override ImmutableArray SupportedDiagnostics => [Descriptors.LuceneDev1003_ArrayMethodParameter]; public override void Initialize(AnalysisContext context) { @@ -43,7 +52,7 @@ private static void AnalyzeNodeCS(SyntaxNodeAnalysisContext context) if (arrayTypeSyntax.ElementType is PredefinedTypeSyntax predefinedTypeSyntax) { if (predefinedTypeSyntax.Keyword.ValueText != "char") - context.ReportDiagnostic(Diagnostic.Create(Rule, parameter.GetLocation(), parameter.ToString())); + context.ReportDiagnostic(Diagnostic.Create(Descriptors.LuceneDev1003_ArrayMethodParameter, parameter.GetLocation(), parameter.ToString())); } } } diff --git a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1004_ArrayMethodReturnValueCSCodeAnalyzer.cs b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1004_ArrayMethodReturnValueCSCodeAnalyzer.cs similarity index 52% rename from src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1004_ArrayMethodReturnValueCSCodeAnalyzer.cs rename to src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1004_ArrayMethodReturnValueCSCodeAnalyzer.cs index c3e1a76..f660905 100644 --- a/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1004_ArrayMethodReturnValueCSCodeAnalyzer.cs +++ b/src/Lucene.Net.CodeAnalysis.Dev/LuceneDev1xxx/LuceneDev1004_ArrayMethodReturnValueCSCodeAnalyzer.cs @@ -1,32 +1,35 @@ -using Lucene.Net.CodeAnalysis.Dev.Helpers; +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +using Lucene.Net.CodeAnalysis.Dev.Utility; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -using System; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; -using System.Reflection.Metadata; -using System.Threading; namespace Lucene.Net.CodeAnalysis.Dev { [DiagnosticAnalyzer(LanguageNames.CSharp)] public class LuceneDev1004_ArrayMethodReturnValueCSCodeAnalyzer : DiagnosticAnalyzer { - public const string DiagnosticId = "LuceneDev1004"; - - // You can change these strings in the Resources.resx file. If you do not want your analyzer to be localize-able, you can use regular strings for Title and MessageFormat. - // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/Localizing%20Analyzers.md for more on localization - private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.LuceneDev1004_AnalyzerTitle), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.LuceneDev1004_AnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources)); - private static readonly LocalizableString Description = new LocalizableResourceString(nameof(Resources.LuceneDev1004_AnalyzerDescription), Resources.ResourceManager, typeof(Resources)); - private const string Category = "Design"; - - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description); - - public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } } + public override ImmutableArray SupportedDiagnostics => [Descriptors.LuceneDev1004_ArrayMethodReturnValue]; public override void Initialize(AnalysisContext context) { @@ -47,7 +50,7 @@ private static void AnalyzeNodeCS(SyntaxNodeAnalysisContext context) if (arrayTypeSyntax.ElementType is PredefinedTypeSyntax predefinedTypeSyntax) { if (predefinedTypeSyntax.Keyword.ValueText != "char") - context.ReportDiagnostic(Diagnostic.Create(Rule, arrayTypeSyntax.GetLocation(), arrayTypeSyntax.ToString())); + context.ReportDiagnostic(Diagnostic.Create(Descriptors.LuceneDev1004_ArrayMethodReturnValue, arrayTypeSyntax.GetLocation(), arrayTypeSyntax.ToString())); } } } diff --git a/src/Lucene.Net.CodeAnalysis.Dev/Properties/launchSettings.json b/src/Lucene.Net.CodeAnalysis.Dev/Properties/launchSettings.json new file mode 100644 index 0000000..22c69f8 --- /dev/null +++ b/src/Lucene.Net.CodeAnalysis.Dev/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "DebugRoslynAnalyzers": { + "commandName": "DebugRoslynComponent", + "targetProject": "../Lucene.Net.CodeAnalysis.Dev.Sample/Lucene.Net.CodeAnalysis.Dev.Sample.csproj" + } + } +} diff --git a/src/Lucene.Net.CodeAnalysis.Dev/Resources.Designer.cs b/src/Lucene.Net.CodeAnalysis.Dev/Resources.Designer.cs deleted file mode 100644 index b8e5f02..0000000 --- a/src/Lucene.Net.CodeAnalysis.Dev/Resources.Designer.cs +++ /dev/null @@ -1,198 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Lucene.Net.CodeAnalysis.Dev { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Lucene.Net.CodeAnalysis.Dev.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to Floating point types should not be compared for exact equality.. - /// - internal static string LuceneDev1000_AnalyzerDescription { - get { - return ResourceManager.GetString("LuceneDev1000_AnalyzerDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to '{0}' may fail due to JIT optimizations. Floating point types should not be compared for exact equality.. - /// - internal static string LuceneDev1000_AnalyzerMessageFormat { - get { - return ResourceManager.GetString("LuceneDev1000_AnalyzerMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Floating point types should not be compared for exact equality.. - /// - internal static string LuceneDev1000_AnalyzerTitle { - get { - return ResourceManager.GetString("LuceneDev1000_AnalyzerTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Floating point types should be formatted with J2N.Numerics.Single.ToString() or J2N.Numerics.Double.ToString().. - /// - internal static string LuceneDev1001_AnalyzerDescription { - get { - return ResourceManager.GetString("LuceneDev1001_AnalyzerDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to '{0}' may fail due to floating point precision issues on .NET Framework and .NET Core prior to version 3.0. Floating point types should be formatted with J2N.Numerics.Single.ToString() or J2N.Numerics.Double.ToString().. - /// - internal static string LuceneDev1001_AnalyzerMessageFormat { - get { - return ResourceManager.GetString("LuceneDev1001_AnalyzerMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Floating point types should be formatted with J2N.Numerics.Single.ToString() or J2N.Numerics.Double.ToString().. - /// - internal static string LuceneDev1001_AnalyzerTitle { - get { - return ResourceManager.GetString("LuceneDev1001_AnalyzerTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Floating point type arithmetic needs to be checked on x86 in .NET Framework.. - /// - internal static string LuceneDev1002_AnalyzerDescription { - get { - return ResourceManager.GetString("LuceneDev1002_AnalyzerDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to '{0}' may fail due to floating point precision issues on .NET Framework and .NET Core prior to version 3.0. Floating point type arithmetic needs to be checked on x86 in .NET Framework and may require extra casting.. - /// - internal static string LuceneDev1002_AnalyzerMessageFormat { - get { - return ResourceManager.GetString("LuceneDev1002_AnalyzerMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Floating point type arithmetic needs to be checked on x86 in .NET Framework.. - /// - internal static string LuceneDev1002_AnalyzerTitle { - get { - return ResourceManager.GetString("LuceneDev1002_AnalyzerTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Method parameters that accept array types should be analyzed to determine whether they are better suited to be ref or out parameters.. - /// - internal static string LuceneDev1003_AnalyzerDescription { - get { - return ResourceManager.GetString("LuceneDev1003_AnalyzerDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to '{0}' needs to be analyzed to determine whether the array can be replaced with a ref or out parameter.. - /// - internal static string LuceneDev1003_AnalyzerMessageFormat { - get { - return ResourceManager.GetString("LuceneDev1003_AnalyzerMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Method parameters that accept array types should be analyzed to determine whether they are better suited to be ref or out parameters.. - /// - internal static string LuceneDev1003_AnalyzerTitle { - get { - return ResourceManager.GetString("LuceneDev1003_AnalyzerTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Methods that return array types should be analyzed to determine whether they are better suited to be one or more out parameters or to return a ValueTuple.. - /// - internal static string LuceneDev1004_AnalyzerDescription { - get { - return ResourceManager.GetString("LuceneDev1004_AnalyzerDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to '{0}' return type needs to be analyzed to determine whether the array return value can be replaced with one or more out parameters or a return ValueTuple instead of an array to avoid the heap allocation.. - /// - internal static string LuceneDev1004_AnalyzerMessageFormat { - get { - return ResourceManager.GetString("LuceneDev1004_AnalyzerMessageFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Methods that return array types should be analyzed to determine whether they are better suited to be one or more out parameters or to return a ValueTuple.. - /// - internal static string LuceneDev1004_AnalyzerTitle { - get { - return ResourceManager.GetString("LuceneDev1004_AnalyzerTitle", resourceCulture); - } - } - } -} diff --git a/src/Lucene.Net.CodeAnalysis.Dev/Resources.resx b/src/Lucene.Net.CodeAnalysis.Dev/Resources.resx index 7644468..d61ab13 100644 --- a/src/Lucene.Net.CodeAnalysis.Dev/Resources.resx +++ b/src/Lucene.Net.CodeAnalysis.Dev/Resources.resx @@ -1,17 +1,37 @@ + + + - + --> @@ -126,7 +146,7 @@ The format-able message the diagnostic displays. - Floating point types should not be compared for exact equality. + Floating point types should not be compared for exact equality The title of the diagnostic. @@ -138,7 +158,7 @@ The format-able message the diagnostic displays. - Floating point types should be formatted with J2N.Numerics.Single.ToString() or J2N.Numerics.Double.ToString(). + Floating point types should be formatted with J2N Numerics Single or Double ToString methods The title of the diagnostic. @@ -150,7 +170,7 @@ The format-able message the diagnostic displays. - Floating point type arithmetic needs to be checked on x86 in .NET Framework. + Floating point type arithmetic needs to be checked on some x86 platforms The title of the diagnostic. @@ -158,11 +178,11 @@ Java is lacking ref and out parameters. As a result, code ported from Java may use parameters that accept a 1-dimensional array in order to facilitate returning a reference to a value type. In .NET these hacks are unnecessary and these parameters should be converted to ref, out, or ValueTuple parameters. - '{0}' needs to be analyzed to determine whether the array can be replaced with a ref or out parameter. + '{0}' needs to be analyzed to determine whether the array can be replaced with a ref or out parameter The format-able message the diagnostic displays. - Method parameters that accept array types should be analyzed to determine whether they are better suited to be ref or out parameters. + Method parameters that accept array types should be analyzed to determine whether they are better suited to be ref or out parameters The title of the diagnostic. @@ -170,11 +190,11 @@ Java is lacking ref and out parameters. As a result, code ported from Java may use methods that return a 1-dimensional array in order to facilitate returning a reference to multiple value types. In .NET these hacks are unnecessary and these return values should be converted to out parameters or return a ValueTuple. - '{0}' return type needs to be analyzed to determine whether the array return value can be replaced with one or more out parameters or a return ValueTuple instead of an array to avoid the heap allocation. + '{0}' return type needs to be analyzed to determine whether the array return value can be replaced with one or more out parameters or a return ValueTuple instead of an array to avoid the heap allocation The format-able message the diagnostic displays. - Methods that return array types should be analyzed to determine whether they are better suited to be one or more out parameters or to return a ValueTuple. + Methods that return array types should be analyzed to determine whether they are better suited to be one or more out parameters or to return a ValueTuple The title of the diagnostic. - \ No newline at end of file + diff --git a/src/Lucene.Net.CodeAnalysis.Dev/Utility/Category.cs b/src/Lucene.Net.CodeAnalysis.Dev/Utility/Category.cs new file mode 100644 index 0000000..21b6be6 --- /dev/null +++ b/src/Lucene.Net.CodeAnalysis.Dev/Utility/Category.cs @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Lucene.Net.CodeAnalysis.Dev.Utility +{ + public enum Category + { + Design, + Globalization, + Mobility, + Performance, + Security, + Usage, + Naming, + Interoperability, + Maintainability, + Reliability, + Documentation, + } +} diff --git a/src/Lucene.Net.CodeAnalysis.Dev/Utility/Descriptors.LuceneDev1xxx.cs b/src/Lucene.Net.CodeAnalysis.Dev/Utility/Descriptors.LuceneDev1xxx.cs new file mode 100644 index 0000000..471a667 --- /dev/null +++ b/src/Lucene.Net.CodeAnalysis.Dev/Utility/Descriptors.LuceneDev1xxx.cs @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +using Microsoft.CodeAnalysis; +using static Microsoft.CodeAnalysis.DiagnosticSeverity; +using static Lucene.Net.CodeAnalysis.Dev.Utility.Category; + +namespace Lucene.Net.CodeAnalysis.Dev.Utility +{ + public static partial class Descriptors + { + // IMPORTANT: Do not make these into properties! The AnalyzerReleases release management + // analyzers do not recognize them and will report RS2002 warnings if it cannot read the + // DignosticDescriptor instance through a field. + + public static readonly DiagnosticDescriptor LuceneDev1000_FloatingPointEquality = + Diagnostic( + "LuceneDev1000", + Design, + Warning + ); + + public static readonly DiagnosticDescriptor LuceneDev1001_FloatingPointFormatting = + Diagnostic( + "LuceneDev1001", + Design, + Warning + ); + + public static readonly DiagnosticDescriptor LuceneDev1002_FloatingPointArithmetic = + Diagnostic( + "LuceneDev1002", + Design, + Warning + ); + + public static readonly DiagnosticDescriptor LuceneDev1003_ArrayMethodParameter = + Diagnostic( + "LuceneDev1003", + Design, + Warning + ); + + public static readonly DiagnosticDescriptor LuceneDev1004_ArrayMethodReturnValue = + Diagnostic( + "LuceneDev1004", + Design, + Warning + ); + } +} diff --git a/src/Lucene.Net.CodeAnalysis.Dev/Utility/Descriptors.cs b/src/Lucene.Net.CodeAnalysis.Dev/Utility/Descriptors.cs new file mode 100644 index 0000000..629c56c --- /dev/null +++ b/src/Lucene.Net.CodeAnalysis.Dev/Utility/Descriptors.cs @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +using Microsoft.CodeAnalysis; +using System.Collections.Concurrent; + +namespace Lucene.Net.CodeAnalysis.Dev.Utility +{ + public static partial class Descriptors + { + static readonly ConcurrentDictionary categoryMapping = new(); + + static DiagnosticDescriptor Diagnostic( + string id, + Category category, + DiagnosticSeverity defaultSeverity) + => Diagnostic(id, category, defaultSeverity, isEnabledByDefault: true); + + static DiagnosticDescriptor Diagnostic( + string id, + Category category, + DiagnosticSeverity defaultSeverity, + bool isEnabledByDefault) + { + //string? helpLink = null; + var categoryString = categoryMapping.GetOrAdd(category, c => c.ToString()); + + var title = new LocalizableResourceString($"{id}_AnalyzerTitle", Resources.ResourceManager, typeof(Resources)); + var messageFormat = new LocalizableResourceString($"{id}_AnalyzerMessageFormat", Resources.ResourceManager, typeof(Resources)); + var description = new LocalizableResourceString($"{id}_AnalyzerDescription", Resources.ResourceManager, typeof(Resources)); + + //return new DiagnosticDescriptor(id, title, messageFormat, categoryString, defaultSeverity, isEnabledByDefault: true, helpLinkUri: helpLink); + return new DiagnosticDescriptor(id, title, messageFormat, categoryString, defaultSeverity, isEnabledByDefault); + } + } +} diff --git a/src/Lucene.Net.CodeAnalysis.Dev/Helpers/FloatingPoint.cs b/src/Lucene.Net.CodeAnalysis.Dev/Utility/FloatingPoint.cs similarity index 59% rename from src/Lucene.Net.CodeAnalysis.Dev/Helpers/FloatingPoint.cs rename to src/Lucene.Net.CodeAnalysis.Dev/Utility/FloatingPoint.cs index 15038f0..f9fbe97 100644 --- a/src/Lucene.Net.CodeAnalysis.Dev/Helpers/FloatingPoint.cs +++ b/src/Lucene.Net.CodeAnalysis.Dev/Utility/FloatingPoint.cs @@ -1,6 +1,25 @@ -using Microsoft.CodeAnalysis; +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ -namespace Lucene.Net.CodeAnalysis.Dev.Helpers +using Microsoft.CodeAnalysis; + +namespace Lucene.Net.CodeAnalysis.Dev.Utility { internal static class FloatingPoint { diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props new file mode 100644 index 0000000..e4914c2 --- /dev/null +++ b/tests/Directory.Build.props @@ -0,0 +1,28 @@ + + + + + + diff --git a/tests/Directory.Build.targets b/tests/Directory.Build.targets new file mode 100644 index 0000000..4b8366d --- /dev/null +++ b/tests/Directory.Build.targets @@ -0,0 +1,35 @@ + + + + + + + + + + + + + true + + + diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.CodeFixes.Tests/Lucene.Net.CodeAnalysis.Dev.CodeFixes.Tests.csproj b/tests/Lucene.Net.CodeAnalysis.Dev.CodeFixes.Tests/Lucene.Net.CodeAnalysis.Dev.CodeFixes.Tests.csproj new file mode 100644 index 0000000..9a30a90 --- /dev/null +++ b/tests/Lucene.Net.CodeAnalysis.Dev.CodeFixes.Tests/Lucene.Net.CodeAnalysis.Dev.CodeFixes.Tests.csproj @@ -0,0 +1,41 @@ + + + + + + + net8.0 + enable + enable + true + Lucene.Net.CodeAnalysis.Dev.CodeFixes + + + + + + + + + + + + + diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.CodeFixes.Tests/LuceneDev1xxx/TestLuceneDev1001_FloatingPointFormattingCSCodeFixProvider.cs b/tests/Lucene.Net.CodeAnalysis.Dev.CodeFixes.Tests/LuceneDev1xxx/TestLuceneDev1001_FloatingPointFormattingCSCodeFixProvider.cs new file mode 100644 index 0000000..bf1ddbb --- /dev/null +++ b/tests/Lucene.Net.CodeAnalysis.Dev.CodeFixes.Tests/LuceneDev1xxx/TestLuceneDev1001_FloatingPointFormattingCSCodeFixProvider.cs @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Lucene.Net.CodeAnalysis.Dev.TestUtilities; +using Lucene.Net.CodeAnalysis.Dev.Utility; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Testing; +using NUnit.Framework; + +namespace Lucene.Net.CodeAnalysis.Dev.CodeFixes +{ + public class TestLuceneDev1001_FloatingPointFormattingCSCodeFixProvider + { + [Test] + public async Task TestFix_Float_ToString() + { + var testCode = @" +using System; +using System.Globalization; + +// Mock for J2N.Numerics so we don't need to reference it +namespace J2N.Numerics +{ + public static class Single + { + public static string ToString(float f, IFormatProvider provider) => string.Empty; + } + + public static class Double + { + public static string ToString(double d, IFormatProvider provider) => string.Empty; + } +} + +public class MyClass +{ + private readonly float float1 = 1f; + + public void MyMethod() + { + string result = float1.ToString(CultureInfo.InvariantCulture); + } +}"; + + var fixedCode = @" +using System; +using System.Globalization; + +// Mock for J2N.Numerics so we don't need to reference it +namespace J2N.Numerics +{ + public static class Single + { + public static string ToString(float f, IFormatProvider provider) => string.Empty; + } + + public static class Double + { + public static string ToString(double d, IFormatProvider provider) => string.Empty; + } +} + +public class MyClass +{ + private readonly float float1 = 1f; + + public void MyMethod() + { + string result = J2N.Numerics.Single.ToString(float1, CultureInfo.InvariantCulture); + } +}"; + + var expected = new DiagnosticResult(Descriptors.LuceneDev1001_FloatingPointFormatting) + .WithSeverity(DiagnosticSeverity.Warning) + .WithMessageFormat(Descriptors.LuceneDev1001_FloatingPointFormatting.MessageFormat) + .WithArguments("float1.ToString") + .WithLocation("/0/Test0.cs", line: 25, column: 25); + + var test = new InjectableCodeFixTest( + () => new LuceneDev1001_FloatingPointFormattingCSCodeAnalyzer(), + () => new LuceneDev1001_FloatingPointFormattingCSCodeFixProvider()) + { + TestCode = testCode, + FixedCode = fixedCode, + ExpectedDiagnostics = { expected } + }; + + await test.RunAsync(); + } + + [Test] + public async Task TestFix_Double_ToString() + { + var testCode = @" +using System; +using System.Globalization; + +// Mock for J2N.Numerics so we don't need to reference it +namespace J2N.Numerics +{ + public static class Single + { + public static string ToString(float f, IFormatProvider provider) => string.Empty; + } + + public static class Double + { + public static string ToString(double d, IFormatProvider provider) => string.Empty; + } +} + +public class MyClass +{ + private readonly double double1 = 1.0; + + public void MyMethod() + { + string result = double1.ToString(CultureInfo.InvariantCulture); + } +}"; + + var fixedCode = @" +using System; +using System.Globalization; + +// Mock for J2N.Numerics so we don't need to reference it +namespace J2N.Numerics +{ + public static class Single + { + public static string ToString(float f, IFormatProvider provider) => string.Empty; + } + + public static class Double + { + public static string ToString(double d, IFormatProvider provider) => string.Empty; + } +} + +public class MyClass +{ + private readonly double double1 = 1.0; + + public void MyMethod() + { + string result = J2N.Numerics.Double.ToString(double1, CultureInfo.InvariantCulture); + } +}"; + + var expected = new DiagnosticResult(Descriptors.LuceneDev1001_FloatingPointFormatting) + .WithSeverity(DiagnosticSeverity.Warning) + .WithMessageFormat(Descriptors.LuceneDev1001_FloatingPointFormatting.MessageFormat) + .WithArguments("double1.ToString") + .WithLocation("/0/Test0.cs", line: 25, column: 25); + + var test = new InjectableCodeFixTest( + () => new LuceneDev1001_FloatingPointFormattingCSCodeAnalyzer(), + () => new LuceneDev1001_FloatingPointFormattingCSCodeFixProvider()) + { + TestCode = testCode, + FixedCode = fixedCode, + ExpectedDiagnostics = { expected } + }; + + await test.RunAsync(); + } + } +} diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.TestUtilities/InjectableAnalyzerTest.cs b/tests/Lucene.Net.CodeAnalysis.Dev.TestUtilities/InjectableAnalyzerTest.cs new file mode 100644 index 0000000..37ca8ef --- /dev/null +++ b/tests/Lucene.Net.CodeAnalysis.Dev.TestUtilities/InjectableAnalyzerTest.cs @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; + +namespace Lucene.Net.CodeAnalysis.Dev.TestUtilities +{ + public class InjectableCSharpAnalyzerTest : AnalyzerTest + { + private readonly Func analyzerFactory; + + public InjectableCSharpAnalyzerTest(Func analyzerFactory) + { + this.analyzerFactory = analyzerFactory ?? throw new ArgumentNullException(nameof(analyzerFactory)); + } + + public override string Language => LanguageNames.CSharp; + + protected override string DefaultFileExt => "cs"; + + protected override CompilationOptions CreateCompilationOptions() + { + return new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); + } + + protected override ParseOptions CreateParseOptions() + { + return new CSharpParseOptions(LanguageVersion.Latest, DocumentationMode.Diagnose); + } + + protected override IEnumerable GetDiagnosticAnalyzers() + { + yield return analyzerFactory(); + } + } +} diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.TestUtilities/InjectableCodeFixTest.cs b/tests/Lucene.Net.CodeAnalysis.Dev.TestUtilities/InjectableCodeFixTest.cs new file mode 100644 index 0000000..8664b0c --- /dev/null +++ b/tests/Lucene.Net.CodeAnalysis.Dev.TestUtilities/InjectableCodeFixTest.cs @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; + +namespace Lucene.Net.CodeAnalysis.Dev.TestUtilities +{ + public class InjectableCodeFixTest : CodeFixTest + { + private readonly Func analyzerFactory; + private readonly Func codeFixFactory; + + public InjectableCodeFixTest(Func analyzerFactory, Func codeFixFactory) + { + this.analyzerFactory = analyzerFactory ?? throw new ArgumentNullException(nameof(analyzerFactory)); + this.codeFixFactory = codeFixFactory ?? throw new ArgumentNullException(nameof(codeFixFactory)); + } + + public override Type SyntaxKindType => typeof(SyntaxKind); + + public override string Language => LanguageNames.CSharp; + + protected override string DefaultFileExt => "cs"; + + protected override CompilationOptions CreateCompilationOptions() + { + return new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); + } + + protected override ParseOptions CreateParseOptions() + { + return new CSharpParseOptions(LanguageVersion.Latest, DocumentationMode.Diagnose); + } + + protected override IEnumerable GetCodeFixProviders() + { + yield return codeFixFactory(); + } + + protected override IEnumerable GetDiagnosticAnalyzers() + { + yield return analyzerFactory(); + } + } +} diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.TestUtilities/Lucene.Net.CodeAnalysis.Dev.TestUtilities.csproj b/tests/Lucene.Net.CodeAnalysis.Dev.TestUtilities/Lucene.Net.CodeAnalysis.Dev.TestUtilities.csproj new file mode 100644 index 0000000..a9432c9 --- /dev/null +++ b/tests/Lucene.Net.CodeAnalysis.Dev.TestUtilities/Lucene.Net.CodeAnalysis.Dev.TestUtilities.csproj @@ -0,0 +1,36 @@ + + + + + + + net8.0 + enable + enable + + + + + + + + + + diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.TestUtilities/Verifier.cs b/tests/Lucene.Net.CodeAnalysis.Dev.TestUtilities/Verifier.cs new file mode 100644 index 0000000..7b45bcf --- /dev/null +++ b/tests/Lucene.Net.CodeAnalysis.Dev.TestUtilities/Verifier.cs @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Testing; +using NUnit.Framework; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; + +namespace Lucene.Net.CodeAnalysis.Dev.TestUtilities +{ + public class Verifier : IVerifier + { + public Verifier() + : this(ImmutableStack.Empty) + { + } + + public Verifier(ImmutableStack context) + { + Context = context ?? throw new ArgumentNullException(nameof(context)); + } + + protected ImmutableStack Context { get; } + + public virtual void Empty(string collectionName, IEnumerable collection) + { + Assert.That(collection, Is.Empty, CreateMessage($"Expected '{collectionName}' to be empty, contains '{collection?.Count()}' elements")); + } + + public virtual void Equal(T expected, T actual, string? message = null) + { + if (message is null && Context.IsEmpty) + { + Assert.That(actual, Is.EqualTo(expected)); + } + else + { + Assert.That(actual, Is.EqualTo(expected), CreateMessage(message!)); + } + } + + public virtual void True([DoesNotReturnIf(false)] bool assert, string? message = null) + { + if (message is null && Context.IsEmpty) + { + Assert.That(assert); + } + else + { + Assert.That(assert, CreateMessage(message!)); + } + } + + public virtual void False([DoesNotReturnIf(true)] bool assert, string? message = null) + { + if (message is null && Context.IsEmpty) + { + Assert.That(assert, Is.False); + } + else + { + Assert.That(assert, Is.False, CreateMessage(message!)); + } + } + + [DoesNotReturn] + public virtual void Fail(string? message = null) + { + if (message is null && Context.IsEmpty) + { + Assert.Fail(); + } + else + { + Assert.Fail(CreateMessage(message!)); + } + + throw new InvalidOperationException("This program location is thought to be unreachable."); + } + + public virtual void LanguageIsSupported(string language) + { + Assert.That(language != LanguageNames.CSharp && language != LanguageNames.VisualBasic, Is.False, CreateMessage($"Unsupported Language: '{language}'")); + } + + public virtual void NotEmpty(string collectionName, IEnumerable collection) + { + Assert.That(collection, Is.Not.Empty, CreateMessage($"expected '{collectionName}' to be non-empty, contains")); + } + + public virtual void SequenceEqual(IEnumerable expected, IEnumerable actual, IEqualityComparer? equalityComparer = null, string? message = null) + { + var comparer = new SequenceEqualEnumerableEqualityComparer(equalityComparer); + var areEqual = comparer.Equals(expected, actual); + if (!areEqual) + { + Assert.Fail(CreateMessage(message!)); + } + } + + public virtual IVerifier PushContext(string context) + { + Assert.That(GetType(), Is.EqualTo(typeof(Verifier))); + return new Verifier(Context.Push(context)); + } + + protected virtual string CreateMessage(string? message) + { + foreach (var frame in Context) + { + message = "Context: " + frame + Environment.NewLine + message; + } + + return message ?? string.Empty; + } + + private sealed class SequenceEqualEnumerableEqualityComparer : IEqualityComparer> + { + private readonly IEqualityComparer _itemEqualityComparer; + + public SequenceEqualEnumerableEqualityComparer(IEqualityComparer? itemEqualityComparer) + { + _itemEqualityComparer = itemEqualityComparer ?? EqualityComparer.Default; + } + + public bool Equals(IEnumerable? x, IEnumerable? y) + { + if (ReferenceEquals(x, y)) { return true; } + if (x is null || y is null) { return false; } + + return x.SequenceEqual(y, _itemEqualityComparer); + } + + public int GetHashCode(IEnumerable obj) + { + if (obj is null) + { + return 0; + } + + // From System.Tuple + // + // The suppression is required due to an invalid contract in IEqualityComparer + // https://github.com/dotnet/runtime/issues/30998 + return obj + .Select(item => _itemEqualityComparer.GetHashCode(item!)) + .Aggregate( + 0, + (aggHash, nextHash) => (aggHash << 5) + aggHash ^ nextHash); + } + } + } +} diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.Tests/Lucene.Net.CodeAnalysis.Dev.Tests.csproj b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/Lucene.Net.CodeAnalysis.Dev.Tests.csproj new file mode 100644 index 0000000..69570fd --- /dev/null +++ b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/Lucene.Net.CodeAnalysis.Dev.Tests.csproj @@ -0,0 +1,40 @@ + + + + + + + net8.0 + enable + true + Lucene.Net.CodeAnalysis.Dev + + + + + + + + + + + + + diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1000_FloatingPointEqualityCSCodeAnalyzer.cs b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1000_FloatingPointEqualityCSCodeAnalyzer.cs new file mode 100644 index 0000000..99753cc --- /dev/null +++ b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1000_FloatingPointEqualityCSCodeAnalyzer.cs @@ -0,0 +1,388 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Lucene.Net.CodeAnalysis.Dev.TestUtilities; +using Lucene.Net.CodeAnalysis.Dev.Utility; +using Microsoft.CodeAnalysis.Testing; +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Lucene.Net.CodeAnalysis.Dev +{ + public class TestLuceneDev1000_FloatingPointEqualityCSCodeAnalyzer + { + //No diagnostics expected to show up + [Test] + public async Task TestEmptyFile() + { + var testCode = @""; + + var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer()) + { + TestCode = testCode + }; + + await test.RunAsync(); + } + + [Test] + public async Task TestDiagnostic_LessThan() + { + var testCode = @" + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using System.Diagnostics; + + public class MyClass + { + public void MyMethod() + { + } + public void MyMethod(int n) + { + } + protected internal bool LessThan(float termA, float termB) + { + return termA < termB; + } + } + "; + + var expected = DiagnosticResult.CompilerWarning(Descriptors.LuceneDev1000_FloatingPointEquality.Id) + .WithMessageFormat(Descriptors.LuceneDev1000_FloatingPointEquality.MessageFormat) + .WithArguments("termA < termB") + .WithLocation("/0/Test0.cs", line: 19, column: 24); + + var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer()) + { + TestCode = testCode, + ExpectedDiagnostics = { expected } + }; + + await test.RunAsync(); + } + + [Test] + public async Task TestDiagnostic_EqualTo() + { + var testCode = @" + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using System.Diagnostics; + + public class MyClass + { + public void MyMethod() + { + } + public void MyMethod(int n) + { + } + protected internal bool LessThan(float termA, float termB) + { + return termA == termB; + } + } + "; + + var expected = DiagnosticResult.CompilerWarning(Descriptors.LuceneDev1000_FloatingPointEquality.Id) + .WithMessageFormat(Descriptors.LuceneDev1000_FloatingPointEquality.MessageFormat) + .WithArguments("termA == termB") + .WithLocation("/0/Test0.cs", line: 19, column: 24); + + var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer()) + { + TestCode = testCode, + ExpectedDiagnostics = { expected } + }; + + await test.RunAsync(); + } + + [Test] + public async Task TestDiagnostic_GreaterThan() + { + var testCode = @" + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using System.Diagnostics; + + public class MyClass + { + public void MyMethod() + { + } + public void MyMethod(int n) + { + } + protected internal bool LessThan(float termA, float termB) + { + return termA > termB; + } + } + "; + + var expected = DiagnosticResult.CompilerWarning(Descriptors.LuceneDev1000_FloatingPointEquality.Id) + .WithMessageFormat(Descriptors.LuceneDev1000_FloatingPointEquality.MessageFormat) + .WithArguments("termA > termB") + .WithLocation("/0/Test0.cs", line: 19, column: 24); + + var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer()) + { + TestCode = testCode, + ExpectedDiagnostics = { expected } + }; + + await test.RunAsync(); + } + + [Test] + public async Task TestDiagnostic_LessThanOrEqualTo() + { + var testCode = @" + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using System.Diagnostics; + + public class MyClass + { + public void MyMethod() + { + } + public void MyMethod(int n) + { + } + protected internal bool LessThan(float termA, float termB) + { + return termA <= termB; + } + } + "; + + var expected = DiagnosticResult.CompilerWarning(Descriptors.LuceneDev1000_FloatingPointEquality.Id) + .WithMessageFormat(Descriptors.LuceneDev1000_FloatingPointEquality.MessageFormat) + .WithArguments("termA <= termB") + .WithLocation("/0/Test0.cs", line: 19, column: 24); + + var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer()) + { + TestCode = testCode, + ExpectedDiagnostics = { expected } + }; + + await test.RunAsync(); + } + + [Test] + public async Task TestDiagnostic_GreaterThanOrEqualTo() + { + var testCode = @" + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using System.Diagnostics; + + public class MyClass + { + public void MyMethod() + { + } + public void MyMethod(int n) + { + } + protected internal bool LessThan(float termA, float termB) + { + return termA >= termB; + } + } + "; + + var expected = DiagnosticResult.CompilerWarning(Descriptors.LuceneDev1000_FloatingPointEquality.Id) + .WithMessageFormat(Descriptors.LuceneDev1000_FloatingPointEquality.MessageFormat) + .WithArguments("termA >= termB") + .WithLocation("/0/Test0.cs", line: 19, column: 24); + + var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer()) + { + TestCode = testCode, + ExpectedDiagnostics = { expected } + }; + + await test.RunAsync(); + } + + [Test] + public async Task TestDiagnostic_EqualTo_MemberVariable() + { + var testCode = @" + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using System.Diagnostics; + + public class MyClass + { + private readonly float myFloat1 = 1f; + private readonly float myFloat2 = 1f; + + public void MyMethod() + { + var x = myFloat1 == myFloat2; + } + } + "; + + var expected = DiagnosticResult.CompilerWarning(Descriptors.LuceneDev1000_FloatingPointEquality.Id) + .WithMessageFormat(Descriptors.LuceneDev1000_FloatingPointEquality.MessageFormat) + .WithArguments("myFloat1 == myFloat2") + .WithLocation("/0/Test0.cs", line: 16, column: 25); + + var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer()) + { + TestCode = testCode, + ExpectedDiagnostics = { expected } + }; + + await test.RunAsync(); + } + + [Test] + public async Task TestDiagnostic_EqualTo_LocalVariable() + { + var testCode = @" + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using System.Diagnostics; + + public class MyClass + { + public void MyMethod() + { + float a = 1f; + float b = 1f; + var x = a == b; + } + } + "; + var expected = DiagnosticResult.CompilerWarning(Descriptors.LuceneDev1000_FloatingPointEquality.Id) + .WithMessageFormat(Descriptors.LuceneDev1000_FloatingPointEquality.MessageFormat) + .WithArguments("a == b") + .WithLocation("/0/Test0.cs", line: 15, column: 25); + + var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer()) + { + TestCode = testCode, + ExpectedDiagnostics = { expected } + }; + + await test.RunAsync(); + } + + [Test] + public async Task TestDiagnostic_EqualTo_PropertyOfParameter() + { + var testCode = @" + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using System.Diagnostics; + + public class MyClass + { + public void MyMethod(Term a, Term b) + { + var x = a.Score == b.Score; + } + } + public class Term + { + public float Score { get; set; } + } + "; + + var expected = DiagnosticResult.CompilerWarning(Descriptors.LuceneDev1000_FloatingPointEquality.Id) + .WithMessageFormat(Descriptors.LuceneDev1000_FloatingPointEquality.MessageFormat) + .WithArguments("a.Score == b.Score") + .WithLocation("/0/Test0.cs", line: 13, column: 25); + + var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer()) + { + TestCode = testCode, + ExpectedDiagnostics = { expected } + }; + + await test.RunAsync(); + } + + [Test] + public async Task TestDiagnostic_Equals_LocalVariable() + { + var testCode = @" + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using System.Diagnostics; + + public class MyClass + { + public void MyMethod() + { + float a = 1f; + float b = 1f; + var x = a.Equals(b); + } + } + "; + + var expected = DiagnosticResult.CompilerWarning(Descriptors.LuceneDev1000_FloatingPointEquality.Id) + .WithMessageFormat(Descriptors.LuceneDev1000_FloatingPointEquality.MessageFormat) + .WithArguments("a.Equals") + .WithLocation("/0/Test0.cs", line: 15, column: 25); + + var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer()) + { + TestCode = testCode, + ExpectedDiagnostics = { expected } + }; + + await test.RunAsync(); + } + } +} diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1001_FloatingPointFormattingCSCodeAnalyzer.cs b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1001_FloatingPointFormattingCSCodeAnalyzer.cs new file mode 100644 index 0000000..a786e0d --- /dev/null +++ b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1001_FloatingPointFormattingCSCodeAnalyzer.cs @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Lucene.Net.CodeAnalysis.Dev.TestUtilities; +using Lucene.Net.CodeAnalysis.Dev.Utility; +using Microsoft.CodeAnalysis.Testing; +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Lucene.Net.CodeAnalysis.Dev +{ + public class TestLuceneDev1001_FloatingPointFormattingCSCodeAnalyzer + { + //No diagnostics expected to show up + [Test] + public async Task TestEmptyFile() + { + var testCode = @""; + + var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1001_FloatingPointFormattingCSCodeAnalyzer()) + { + TestCode = testCode + }; + + await test.RunAsync(); + } + + [Test] + public async Task TestDiagnostic_Float_ToString() + { + var testCode = @" + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Linq; + using System.Text; + using System.Diagnostics; + + public class MyClass + { + private readonly float float1 = 1f; + + public void MyMethod() + { + string result = float1.ToString(CultureInfo.InvariantCulture); + } + } + "; + + var expected = DiagnosticResult.CompilerWarning(Descriptors.LuceneDev1001_FloatingPointFormatting.Id) + .WithMessageFormat(Descriptors.LuceneDev1001_FloatingPointFormatting.MessageFormat) + .WithArguments("float1.ToString") + .WithLocation("/0/Test0.cs", line: 15, column: 33); + + var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1001_FloatingPointFormattingCSCodeAnalyzer()) + { + TestCode = testCode, + ExpectedDiagnostics = { expected } + }; + + await test.RunAsync(); + } + } +} diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1002_FloatingPointArithmeticCSCodeAnalyzer.cs b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1002_FloatingPointArithmeticCSCodeAnalyzer.cs new file mode 100644 index 0000000..ed1ce2b --- /dev/null +++ b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1002_FloatingPointArithmeticCSCodeAnalyzer.cs @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Lucene.Net.CodeAnalysis.Dev.TestUtilities; +using Lucene.Net.CodeAnalysis.Dev.Utility; +using Microsoft.CodeAnalysis.Testing; +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Lucene.Net.CodeAnalysis.Dev +{ + public class TestLuceneDev1002_FloatingPointArithmeticCSCodeAnalyzer + { + + //No diagnostics expected to show up + [Test] + public async Task TestEmptyFile() + { + var testCode = @""; + + var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1002_FloatingPointArithmeticCSCodeAnalyzer()) + { + TestCode = testCode + }; + + await test.RunAsync(); + } + + [Test] + public async Task TestDiagnostic_Float_ToString() + { + var testCode = @" + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using System.Diagnostics; + + public class MyClass + { + private readonly float float1 = 1f; + private readonly float float2 = 3.14f; + + public void MyMethod() + { + long foo = 33; + var result = ((double)float1 * (double)float2) / foo; + } + } + "; + + var expected1 = DiagnosticResult.CompilerWarning(Descriptors.LuceneDev1002_FloatingPointArithmetic.Id) + .WithMessageFormat(Descriptors.LuceneDev1002_FloatingPointArithmetic.MessageFormat) + .WithArguments("((double)float1 * (double)float2) / foo") + .WithLocation("/0/Test0.cs", line: 17, column: 30); + + var expected2 = DiagnosticResult.CompilerWarning(Descriptors.LuceneDev1002_FloatingPointArithmetic.Id) + .WithMessageFormat(Descriptors.LuceneDev1002_FloatingPointArithmetic.MessageFormat) + .WithArguments("(double)float1 * (double)float2") + .WithLocation("/0/Test0.cs", line: 17, column: 31); + + var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1002_FloatingPointArithmeticCSCodeAnalyzer()) + { + TestCode = testCode, + ExpectedDiagnostics = { expected1, expected2 } + }; + + await test.RunAsync(); + } + } +} diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1003_ArrayMethodParameterCSCodeAnalyzer.cs b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1003_ArrayMethodParameterCSCodeAnalyzer.cs new file mode 100644 index 0000000..beb09d7 --- /dev/null +++ b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1003_ArrayMethodParameterCSCodeAnalyzer.cs @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Lucene.Net.CodeAnalysis.Dev.TestUtilities; +using Lucene.Net.CodeAnalysis.Dev.Utility; +using Microsoft.CodeAnalysis.Testing; +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Lucene.Net.CodeAnalysis.Dev +{ + public class TestLuceneDev1003_ArrayMethodParameterCSCodeAnalyzer + { + //No diagnostics expected to show up + [Test] + public async Task TestEmptyFile() + { + var testCode = @""; + + var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1003_ArrayMethodParameterCSCodeAnalyzer()) + { + TestCode = testCode + }; + + await test.RunAsync(); + } + + [Test] + public async Task TestDiagnostic_ParseChar_String_Int32Array_Char() + { + var testCode = @" + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using System.Diagnostics; + + public class MyClass + { + public static bool ParseChar(string id, int[] pos, char ch) + { + int start = pos[0]; + //pos[0] = PatternProps.SkipWhiteSpace(id, pos[0]); + if (pos[0] == id.Length || + id[pos[0]] != ch) + { + pos[0] = start; + return false; + } + ++pos[0]; + return true; + } + } + "; + + var expected = DiagnosticResult.CompilerWarning(Descriptors.LuceneDev1003_ArrayMethodParameter.Id) + .WithMessageFormat(Descriptors.LuceneDev1003_ArrayMethodParameter.MessageFormat) + .WithArguments("int[] pos") + .WithLocation("/0/Test0.cs", line: 11, column: 53); + + var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1003_ArrayMethodParameterCSCodeAnalyzer()) + { + TestCode = testCode, + ExpectedDiagnostics = { expected } + }; + + await test.RunAsync(); + } + + [Test] + public async Task TestDiagnostic_ParseChar_String_CharArray_Char() + { + var testCode = @" + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using System.Diagnostics; + + public class MyClass + { + public static bool ParseChar(string id, char[] pos, char ch) + { + char start = pos[0]; + //pos[0] = PatternProps.SkipWhiteSpace(id, pos[0]); + if (pos[0] == id.Length || + id[pos[0]] != ch) + { + pos[0] = start; + return false; + } + ++pos[0]; + return true; + } + } + "; + // We shouldn't trigger a warning on char[] + var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1003_ArrayMethodParameterCSCodeAnalyzer()) + { + TestCode = testCode + }; + + await test.RunAsync(); + } + } +} diff --git a/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1004_ArrayMethodReturnValueCSCodeAnalyzer.cs b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1004_ArrayMethodReturnValueCSCodeAnalyzer.cs new file mode 100644 index 0000000..93513b6 --- /dev/null +++ b/tests/Lucene.Net.CodeAnalysis.Dev.Tests/LuceneDev1xxx/TestLuceneDev1004_ArrayMethodReturnValueCSCodeAnalyzer.cs @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Lucene.Net.CodeAnalysis.Dev.TestUtilities; +using Lucene.Net.CodeAnalysis.Dev.Utility; +using Microsoft.CodeAnalysis.Testing; +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Lucene.Net.CodeAnalysis.Dev +{ + public class TestLuceneDev1004_ArrayMethodReturnValueCSCodeAnalyzer + { + + //No diagnostics expected to show up + [Test] + public async Task TestEmptyFile() + { + var testCode = @""; + + var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1004_ArrayMethodReturnValueCSCodeAnalyzer()) + { + TestCode = testCode + }; + + await test.RunAsync(); + } + + [Test] + public async Task TestDiagnostic_GetVersionByteArrayFromCompactInt32_ByteArrayReturnType() + { + var testCode = @" + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using System.Diagnostics; + + public class MyClass + { + public static byte[] GetVersionByteArrayFromCompactInt32(int version) // ICU4N specific - Renamed from GetVersionByteArrayFromCompactInt + { + return new byte[] { + (byte)(version >> 24), + (byte)(version >> 16), + (byte)(version >> 8), + (byte)(version) + }; + } + } + "; + + + var expected = DiagnosticResult.CompilerWarning(Descriptors.LuceneDev1004_ArrayMethodReturnValue.Id) + .WithMessageFormat(Descriptors.LuceneDev1004_ArrayMethodReturnValue.MessageFormat) + .WithArguments("byte[]") + .WithLocation("/0/Test0.cs", line: 11, column: 27); + + var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1004_ArrayMethodReturnValueCSCodeAnalyzer()) + { + TestCode = testCode, + ExpectedDiagnostics = { expected } + }; + + await test.RunAsync(); + } + + [Test] + public async Task TestDiagnostic_GetVersionCharArrayFromCompactInt32_CharArrayReturnType() + { + var testCode = @" + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using System.Diagnostics; + + public class MyClass + { + public static char[] GetVersionCharArrayFromCompactInt32(int version) + { + return new char[] { + (char)(version >> 24), + (char)(version >> 16), + (char)(version >> 8), + (char)(version) + }; + } + } + "; + + // We shouldn't trigger a warning on char[] + var test = new InjectableCSharpAnalyzerTest(() => new LuceneDev1004_ArrayMethodReturnValueCSCodeAnalyzer()) + { + TestCode = testCode + }; + + await test.RunAsync(); + } + } +} diff --git a/tests/Lucene.Net.Tests.CodeAnalysis.Dev/Helpers/CodeFixVerifier.Helper.cs b/tests/Lucene.Net.Tests.CodeAnalysis.Dev/Helpers/CodeFixVerifier.Helper.cs deleted file mode 100644 index 328d485..0000000 --- a/tests/Lucene.Net.Tests.CodeAnalysis.Dev/Helpers/CodeFixVerifier.Helper.cs +++ /dev/null @@ -1,102 +0,0 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.Simplification; -using System.Collections.Generic; -using System.Linq; -using System.Threading; - -namespace TestHelper -{ - /* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - /// - /// Diagnostic Producer class with extra methods dealing with applying codefixes - /// All methods are static - /// - public abstract partial class CodeFixVerifier : DiagnosticVerifier - { - /// - /// Apply the inputted CodeAction to the inputted document. - /// Meant to be used to apply codefixes. - /// - /// The Document to apply the fix on - /// A CodeAction that will be applied to the Document. - /// A Document with the changes from the CodeAction - private static Document ApplyFix(Document document, CodeAction codeAction) - { - var operations = codeAction.GetOperationsAsync(CancellationToken.None).Result; - var solution = operations.OfType().Single().ChangedSolution; - return solution.GetDocument(document.Id); - } - - /// - /// Compare two collections of Diagnostics,and return a list of any new diagnostics that appear only in the second collection. - /// Note: Considers Diagnostics to be the same if they have the same Ids. In the case of multiple diagnostics with the same Id in a row, - /// this method may not necessarily return the new one. - /// - /// The Diagnostics that existed in the code before the CodeFix was applied - /// The Diagnostics that exist in the code after the CodeFix was applied - /// A list of Diagnostics that only surfaced in the code after the CodeFix was applied - private static IEnumerable GetNewDiagnostics(IEnumerable diagnostics, IEnumerable newDiagnostics) - { - var oldArray = diagnostics.OrderBy(d => d.Location.SourceSpan.Start).ToArray(); - var newArray = newDiagnostics.OrderBy(d => d.Location.SourceSpan.Start).ToArray(); - - int oldIndex = 0; - int newIndex = 0; - - while (newIndex < newArray.Length) - { - if (oldIndex < oldArray.Length && oldArray[oldIndex].Id == newArray[newIndex].Id) - { - ++oldIndex; - ++newIndex; - } - else - { - yield return newArray[newIndex++]; - } - } - } - - /// - /// Get the existing compiler diagnostics on the inputted document. - /// - /// The Document to run the compiler diagnostic analyzers on - /// The compiler diagnostics that were found in the code - private static IEnumerable GetCompilerDiagnostics(Document document) - { - return document.GetSemanticModelAsync().Result.GetDiagnostics(); - } - - /// - /// Given a document, turn it into a string based on the syntax root - /// - /// The Document to be converted to a string - /// A string containing the syntax of the Document after formatting - private static string GetStringFromDocument(Document document) - { - var simplifiedDoc = Simplifier.ReduceAsync(document, Simplifier.Annotation).Result; - var root = simplifiedDoc.GetSyntaxRootAsync().Result; - root = Formatter.Format(root, Formatter.Annotation, simplifiedDoc.Project.Solution.Workspace); - return root.GetText().ToString(); - } - } -} - diff --git a/tests/Lucene.Net.Tests.CodeAnalysis.Dev/Helpers/DiagnosticResult.cs b/tests/Lucene.Net.Tests.CodeAnalysis.Dev/Helpers/DiagnosticResult.cs deleted file mode 100644 index 605e0ba..0000000 --- a/tests/Lucene.Net.Tests.CodeAnalysis.Dev/Helpers/DiagnosticResult.cs +++ /dev/null @@ -1,86 +0,0 @@ -using Microsoft.CodeAnalysis; -using System; -using System.Diagnostics.CodeAnalysis; - -namespace TestHelper -{ - /* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - /// - /// Location where the diagnostic appears, as determined by path, line number, and column number. - /// - [SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "Used for testing")] - public struct DiagnosticResultLocation - { - public DiagnosticResultLocation(string path, int line, int column) - { - if (line < -1) - { - throw new ArgumentOutOfRangeException(nameof(line), "line must be >= -1"); - } - - if (column < -1) - { - throw new ArgumentOutOfRangeException(nameof(column), "column must be >= -1"); - } - - this.Path = path; - this.Line = line; - this.Column = column; - } - - public string Path { get; } - public int Line { get; } - public int Column { get; } - } - - /// - /// Struct that stores information about a Diagnostic appearing in a source - /// - [SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "Used for testing")] - public struct DiagnosticResult - { - private DiagnosticResultLocation[] locations; - - [SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "Used for testing")] - public DiagnosticResultLocation[] Locations - { - get - { - if (this.locations == null) - { - this.locations = Array.Empty(); - } - return this.locations; - } - set => this.locations = value; - } - - public DiagnosticSeverity Severity { get; set; } - - public string Id { get; set; } - - public string Message { get; set; } - - public string Path => this.Locations.Length > 0 ? this.Locations[0].Path : ""; - - public int Line => this.Locations.Length > 0 ? this.Locations[0].Line : -1; - - public int Column => this.Locations.Length > 0 ? this.Locations[0].Column : -1; - } -} diff --git a/tests/Lucene.Net.Tests.CodeAnalysis.Dev/Helpers/DiagnosticVerifier.Helper.cs b/tests/Lucene.Net.Tests.CodeAnalysis.Dev/Helpers/DiagnosticVerifier.Helper.cs deleted file mode 100644 index 893c55b..0000000 --- a/tests/Lucene.Net.Tests.CodeAnalysis.Dev/Helpers/DiagnosticVerifier.Helper.cs +++ /dev/null @@ -1,187 +0,0 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Text; -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; - -namespace TestHelper -{ - /* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - /// - /// Class for turning strings into documents and getting the diagnostics on them - /// All methods are static - /// - public abstract partial class DiagnosticVerifier - { - private static readonly MetadataReference CorlibReference = MetadataReference.CreateFromFile(typeof(object).Assembly.Location); - private static readonly MetadataReference SystemCoreReference = MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location); - private static readonly MetadataReference CSharpSymbolsReference = MetadataReference.CreateFromFile(typeof(CSharpCompilation).Assembly.Location); - private static readonly MetadataReference CodeAnalysisReference = MetadataReference.CreateFromFile(typeof(Compilation).Assembly.Location); - - internal static string DefaultFilePathPrefix = "Test"; - internal static string CSharpDefaultFileExt = "cs"; - internal static string VisualBasicDefaultExt = "vb"; - internal static string TestProjectName = "TestProject"; - - #region Get Diagnostics - - /// - /// Given classes in the form of strings, their language, and an IDiagnosticAnalyzer to apply to it, return the diagnostics found in the string after converting it to a document. - /// - /// Classes in the form of strings - /// The language the source classes are in - /// The analyzer to be run on the sources - /// An IEnumerable of Diagnostics that surfaced in the source code, sorted by Location - private static Diagnostic[] GetSortedDiagnostics(string[] sources, string language, DiagnosticAnalyzer analyzer) - { - return GetSortedDiagnosticsFromDocuments(analyzer, GetDocuments(sources, language)); - } - - /// - /// Given an analyzer and a document to apply it to, run the analyzer and gather an array of diagnostics found in it. - /// The returned diagnostics are then ordered by location in the source document. - /// - /// The analyzer to run on the documents - /// The Documents that the analyzer will be run on - /// An IEnumerable of Diagnostics that surfaced in the source code, sorted by Location - protected static Diagnostic[] GetSortedDiagnosticsFromDocuments(DiagnosticAnalyzer analyzer, Document[] documents) - { - var projects = new HashSet(); - foreach (var document in documents) - { - projects.Add(document.Project); - } - - var diagnostics = new List(); - foreach (var project in projects) - { - var compilationWithAnalyzers = project.GetCompilationAsync().Result.WithAnalyzers(ImmutableArray.Create(analyzer)); - var diags = compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync().Result; - foreach (var diag in diags) - { - if (diag.Location == Location.None || diag.Location.IsInMetadata) - { - diagnostics.Add(diag); - } - else - { - for (int i = 0; i < documents.Length; i++) - { - var document = documents[i]; - var tree = document.GetSyntaxTreeAsync().Result; - if (tree == diag.Location.SourceTree) - { - diagnostics.Add(diag); - } - } - } - } - } - - var results = SortDiagnostics(diagnostics); - diagnostics.Clear(); - return results; - } - - /// - /// Sort diagnostics by location in source document - /// - /// The list of Diagnostics to be sorted - /// An IEnumerable containing the Diagnostics in order of Location - private static Diagnostic[] SortDiagnostics(IEnumerable diagnostics) - { - return diagnostics.OrderBy(d => d.Location.SourceSpan.Start).ToArray(); - } - - #endregion - - #region Set up compilation and documents - /// - /// Given an array of strings as sources and a language, turn them into a project and return the documents and spans of it. - /// - /// Classes in the form of strings - /// The language the source code is in - /// A Tuple containing the Documents produced from the sources and their TextSpans if relevant - private static Document[] GetDocuments(string[] sources, string language) - { - if (language != LanguageNames.CSharp && language != LanguageNames.VisualBasic) - { - throw new ArgumentException("Unsupported Language"); - } - - var project = CreateProject(sources, language); - var documents = project.Documents.ToArray(); - - if (sources.Length != documents.Length) - { - throw new InvalidOperationException("Amount of sources did not match amount of Documents created"); - } - - return documents; - } - - /// - /// Create a Document from a string through creating a project that contains it. - /// - /// Classes in the form of a string - /// The language the source code is in - /// A Document created from the source string - protected static Document CreateDocument(string source, string language = LanguageNames.CSharp) - { - return CreateProject(new[] { source }, language).Documents.First(); - } - - /// - /// Create a project using the inputted strings as sources. - /// - /// Classes in the form of strings - /// The language the source code is in - /// A Project created out of the Documents created from the source strings - private static Project CreateProject(string[] sources, string language = LanguageNames.CSharp) - { - string fileNamePrefix = DefaultFilePathPrefix; - string fileExt = language == LanguageNames.CSharp ? CSharpDefaultFileExt : VisualBasicDefaultExt; - - var projectId = ProjectId.CreateNewId(debugName: TestProjectName); - - var solution = new AdhocWorkspace() - .CurrentSolution - .AddProject(projectId, TestProjectName, TestProjectName, language) - .AddMetadataReference(projectId, CorlibReference) - .AddMetadataReference(projectId, SystemCoreReference) - .AddMetadataReference(projectId, CSharpSymbolsReference) - .AddMetadataReference(projectId, CodeAnalysisReference); - - int count = 0; - foreach (var source in sources) - { - var newFileName = fileNamePrefix + count + "." + fileExt; - var documentId = DocumentId.CreateNewId(projectId, debugName: newFileName); - solution = solution.AddDocument(documentId, newFileName, SourceText.From(source)); - count++; - } - return solution.GetProject(projectId); - } - #endregion - } -} - diff --git a/tests/Lucene.Net.Tests.CodeAnalysis.Dev/Lucene.Net.Tests.CodeAnalysis.Dev.csproj b/tests/Lucene.Net.Tests.CodeAnalysis.Dev/Lucene.Net.Tests.CodeAnalysis.Dev.csproj deleted file mode 100644 index 81a0277..0000000 --- a/tests/Lucene.Net.Tests.CodeAnalysis.Dev/Lucene.Net.Tests.CodeAnalysis.Dev.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - net6.0 - true - - - - - - - - - - - - - - diff --git a/tests/Lucene.Net.Tests.CodeAnalysis.Dev/TestLuceneDev1000_FloatingPointEqualityCSCodeAnalyzer.cs b/tests/Lucene.Net.Tests.CodeAnalysis.Dev/TestLuceneDev1000_FloatingPointEqualityCSCodeAnalyzer.cs deleted file mode 100644 index 1bba34a..0000000 --- a/tests/Lucene.Net.Tests.CodeAnalysis.Dev/TestLuceneDev1000_FloatingPointEqualityCSCodeAnalyzer.cs +++ /dev/null @@ -1,374 +0,0 @@ -using Lucene.Net.CodeAnalysis.Dev; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; -using NUnit.Framework; -using System; -using TestHelper; - -namespace Lucene.Net.Tests.CodeAnalysis.Dev -{ - public class TestLuceneDev1000_FloatingPointEqualityCSCodeAnalyzer : DiagnosticVerifier - { - - protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return new LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer(); - } - - //No diagnostics expected to show up - [Test] - public void TestEmptyFile() - { - var test = @""; - - VerifyCSharpDiagnostic(test); - } - - [Test] - public void TestDiagnostic_LessThan() - { - var test = @" - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading.Tasks; - using System.Diagnostics; - - public class MyClass - { - public void MyMethod() - { - } - public void MyMethod(int n) - { - } - protected internal override bool LessThan(float termA, float termB) - { - return termA < termB; - } - } - "; - - var expected = new DiagnosticResult - { - Id = LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer.DiagnosticId, - Message = string.Format("'{0}' may fail due to JIT optimizations. Floating point types should not be compared for exact equality.", "termA < termB"), - Severity = DiagnosticSeverity.Warning, - Locations = - new[] { - new DiagnosticResultLocation("Test0.cs", 19, 24) - } - }; - - VerifyCSharpDiagnostic(test, expected); - } - - [Test] - public void TestDiagnostic_EqualTo() - { - var test = @" - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading.Tasks; - using System.Diagnostics; - - public class MyClass - { - public void MyMethod() - { - } - public void MyMethod(int n) - { - } - protected internal override bool LessThan(float termA, float termB) - { - return termA == termB; - } - } - "; - - var expected = new DiagnosticResult - { - Id = LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer.DiagnosticId, - Message = string.Format("'{0}' may fail due to JIT optimizations. Floating point types should not be compared for exact equality.", "termA == termB"), - Severity = DiagnosticSeverity.Warning, - Locations = - new[] { - new DiagnosticResultLocation("Test0.cs", 19, 24) - } - }; - - VerifyCSharpDiagnostic(test, expected); - } - - [Test] - public void TestDiagnostic_GreaterThan() - { - var test = @" - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading.Tasks; - using System.Diagnostics; - - public class MyClass - { - public void MyMethod() - { - } - public void MyMethod(int n) - { - } - protected internal override bool LessThan(float termA, float termB) - { - return termA > termB; - } - } - "; - - var expected = new DiagnosticResult - { - Id = LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer.DiagnosticId, - Message = string.Format("'{0}' may fail due to JIT optimizations. Floating point types should not be compared for exact equality.", "termA > termB"), - Severity = DiagnosticSeverity.Warning, - Locations = - new[] { - new DiagnosticResultLocation("Test0.cs", 19, 24) - } - }; - - VerifyCSharpDiagnostic(test, expected); - } - - [Test] - public void TestDiagnostic_LessThanOrEqualTo() - { - var test = @" - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading.Tasks; - using System.Diagnostics; - - public class MyClass - { - public void MyMethod() - { - } - public void MyMethod(int n) - { - } - protected internal override bool LessThan(float termA, float termB) - { - return termA <= termB; - } - } - "; - - var expected = new DiagnosticResult - { - Id = LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer.DiagnosticId, - Message = string.Format("'{0}' may fail due to JIT optimizations. Floating point types should not be compared for exact equality.", "termA <= termB"), - Severity = DiagnosticSeverity.Warning, - Locations = - new[] { - new DiagnosticResultLocation("Test0.cs", 19, 24) - } - }; - - VerifyCSharpDiagnostic(test, expected); - } - - [Test] - public void TestDiagnostic_GreaterThanOrEqualTo() - { - var test = @" - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading.Tasks; - using System.Diagnostics; - - public class MyClass - { - public void MyMethod() - { - } - public void MyMethod(int n) - { - } - protected internal override bool LessThan(float termA, float termB) - { - return termA >= termB; - } - } - "; - - var expected = new DiagnosticResult - { - Id = LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer.DiagnosticId, - Message = string.Format("'{0}' may fail due to JIT optimizations. Floating point types should not be compared for exact equality.", "termA >= termB"), - Severity = DiagnosticSeverity.Warning, - Locations = - new[] { - new DiagnosticResultLocation("Test0.cs", 19, 24) - } - }; - - VerifyCSharpDiagnostic(test, expected); - } - - [Test] - public void TestDiagnostic_EqualTo_MemberVariable() - { - var test = @" - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading.Tasks; - using System.Diagnostics; - - public class MyClass - { - private readonly float myFloat1 = 1f; - private readonly float myFloat2 = 1f; - - public void MyMethod() - { - var x = myFloat1 == myFloat2; - } - } - "; - - var expected = new DiagnosticResult - { - Id = LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer.DiagnosticId, - Message = string.Format("'{0}' may fail due to JIT optimizations. Floating point types should not be compared for exact equality.", "myFloat1 == myFloat2"), - Severity = DiagnosticSeverity.Warning, - Locations = - new[] { - new DiagnosticResultLocation("Test0.cs", 16, 25) - } - }; - - VerifyCSharpDiagnostic(test, expected); - } - - [Test] - public void TestDiagnostic_EqualTo_LocalVariable() - { - var test = @" - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading.Tasks; - using System.Diagnostics; - - public class MyClass - { - public void MyMethod() - { - float a = 1f; - float b = 1f; - var x = a == b; - } - } - "; - - var expected = new DiagnosticResult - { - Id = LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer.DiagnosticId, - Message = string.Format("'{0}' may fail due to JIT optimizations. Floating point types should not be compared for exact equality.", "a == b"), - Severity = DiagnosticSeverity.Warning, - Locations = - new[] { - new DiagnosticResultLocation("Test0.cs", 15, 25) - } - }; - - VerifyCSharpDiagnostic(test, expected); - } - - [Test] - public void TestDiagnostic_EqualTo_PropertyOfParameter() - { - var test = @" - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading.Tasks; - using System.Diagnostics; - - public class MyClass - { - public void MyMethod(Term a, Term b) - { - var x = a.Score == b.Score; - } - } - public class Term - { - public float Score { get; set; } - } - "; - - var expected = new DiagnosticResult - { - Id = LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer.DiagnosticId, - Message = string.Format("'{0}' may fail due to JIT optimizations. Floating point types should not be compared for exact equality.", "a.Score == b.Score"), - Severity = DiagnosticSeverity.Warning, - Locations = - new[] { - new DiagnosticResultLocation("Test0.cs", 13, 25) - } - }; - - VerifyCSharpDiagnostic(test, expected); - } - - [Test] - public void TestDiagnostic_Equals_LocalVariable() - { - var test = @" - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading.Tasks; - using System.Diagnostics; - - public class MyClass - { - public void MyMethod() - { - float a = 1f; - float b = 1f; - var x = a.Equals(b); - } - } - "; - - var expected = new DiagnosticResult - { - Id = LuceneDev1000_FloatingPointEqualityCSCodeAnalyzer.DiagnosticId, - Message = string.Format("'{0}' may fail due to JIT optimizations. Floating point types should not be compared for exact equality.", "a.Equals"), - Severity = DiagnosticSeverity.Warning, - Locations = - new[] { - new DiagnosticResultLocation("Test0.cs", 15, 25) - } - }; - - VerifyCSharpDiagnostic(test, expected); - } - } -} diff --git a/tests/Lucene.Net.Tests.CodeAnalysis.Dev/TestLuceneDev1001_FloatingPointFormattingCSCodeAnalyzer.cs b/tests/Lucene.Net.Tests.CodeAnalysis.Dev/TestLuceneDev1001_FloatingPointFormattingCSCodeAnalyzer.cs deleted file mode 100644 index c8b7373..0000000 --- a/tests/Lucene.Net.Tests.CodeAnalysis.Dev/TestLuceneDev1001_FloatingPointFormattingCSCodeAnalyzer.cs +++ /dev/null @@ -1,66 +0,0 @@ -using Lucene.Net.CodeAnalysis.Dev; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; -using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using TestHelper; - -namespace Lucene.Net.Tests.CodeAnalysis.Dev -{ - public class TestLuceneDev1001_FloatingPointFormattingCSCodeAnalyzer : DiagnosticVerifier - { - protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return new LuceneDev1001_FloatingPointFormattingCSCodeAnalyzer(); - } - - //No diagnostics expected to show up - [Test] - public void TestEmptyFile() - { - var test = @""; - - VerifyCSharpDiagnostic(test); - } - - [Test] - public void TestDiagnostic_Float_ToString() - { - var test = @" - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading.Tasks; - using System.Diagnostics; - - public class MyClass - { - private readonly float float1 = 1f; - - public void MyMethod() - { - string result = float1.ToString(CultureInfo.InvariantCulture); - } - } - "; - - var expected = new DiagnosticResult - { - Id = LuceneDev1001_FloatingPointFormattingCSCodeAnalyzer.DiagnosticId, - Message = string.Format("'{0}' may fail due to floating point precision issues on .NET Framework and .NET Core prior to version 3.0. Floating point types should be formatted with J2N.Numerics.Single.ToString() or J2N.Numerics.Double.ToString().", "float1.ToString"), - Severity = DiagnosticSeverity.Warning, - Locations = - new[] { - new DiagnosticResultLocation("Test0.cs", 15, 33) - } - }; - - VerifyCSharpDiagnostic(test, expected); - } - } -} diff --git a/tests/Lucene.Net.Tests.CodeAnalysis.Dev/TestLuceneDev1002_FloatingPointArithmeticCSCodeAnalyzer.cs b/tests/Lucene.Net.Tests.CodeAnalysis.Dev/TestLuceneDev1002_FloatingPointArithmeticCSCodeAnalyzer.cs deleted file mode 100644 index fe65ea1..0000000 --- a/tests/Lucene.Net.Tests.CodeAnalysis.Dev/TestLuceneDev1002_FloatingPointArithmeticCSCodeAnalyzer.cs +++ /dev/null @@ -1,68 +0,0 @@ -using Lucene.Net.CodeAnalysis.Dev; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; -using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using TestHelper; - -namespace Lucene.Net.Tests.CodeAnalysis.Dev -{ - public class TestLuceneDev1002_FloatingPointArithmeticCSCodeAnalyzer : DiagnosticVerifier - { - protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return new LuceneDev1002_FloatingPointArithmeticCSCodeAnalyzer(); - } - - //No diagnostics expected to show up - [Test] - public void TestEmptyFile() - { - var test = @""; - - VerifyCSharpDiagnostic(test); - } - - [Test] - public void TestDiagnostic_Float_ToString() - { - var test = @" - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading.Tasks; - using System.Diagnostics; - - public class MyClass - { - private readonly float float1 = 1f; - private readonly float float2 = 3.14f; - - public void MyMethod() - { - long foo = 33; - var result = ((double)float1 * (double)float2)) / foo; - } - } - "; - - var expected = new DiagnosticResult - { - Id = LuceneDev1002_FloatingPointArithmeticCSCodeAnalyzer.DiagnosticId, - Message = string.Format("'{0}' may fail due to floating point precision issues on .NET Framework and .NET Core prior to version 3.0. Floating point type arithmetic needs to be checked on x86 in .NET Framework and may require extra casting.", "float1.ToString"), - Severity = DiagnosticSeverity.Warning, - Locations = - new[] { - new DiagnosticResultLocation("Test0.cs", 15, 33) - } - }; - - VerifyCSharpDiagnostic(test, expected); - } - } -} diff --git a/tests/Lucene.Net.Tests.CodeAnalysis.Dev/TestLuceneDev1003_ArrayMethodParameterCSCodeAnalyzer.cs b/tests/Lucene.Net.Tests.CodeAnalysis.Dev/TestLuceneDev1003_ArrayMethodParameterCSCodeAnalyzer.cs deleted file mode 100644 index 640def3..0000000 --- a/tests/Lucene.Net.Tests.CodeAnalysis.Dev/TestLuceneDev1003_ArrayMethodParameterCSCodeAnalyzer.cs +++ /dev/null @@ -1,101 +0,0 @@ -using Lucene.Net.CodeAnalysis.Dev; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; -using NUnit.Framework; -using TestHelper; - -namespace Lucene.Net.Tests.CodeAnalysis.Dev -{ - public class TestLuceneDev1003_ArrayMethodParameterCSCodeAnalyzer : DiagnosticVerifier - { - protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return new LuceneDev1003_ArrayMethodParameterCSCodeAnalyzer(); - } - - //No diagnostics expected to show up - [Test] - public void TestEmptyFile() - { - var test = @""; - - VerifyCSharpDiagnostic(test); - } - - [Test] - public void TestDiagnostic_ParseChar_String_Int32Array_Char() - { - var test = @" - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading.Tasks; - using System.Diagnostics; - - public class MyClass - { - public static bool ParseChar(string id, int[] pos, char ch) - { - int start = pos[0]; - pos[0] = PatternProps.SkipWhiteSpace(id, pos[0]); - if (pos[0] == id.Length || - id[pos[0]] != ch) - { - pos[0] = start; - return false; - } - ++pos[0]; - return true; - } - } - "; - - var expected = new DiagnosticResult - { - Id = LuceneDev1003_ArrayMethodParameterCSCodeAnalyzer.DiagnosticId, - Message = string.Format("'{0}' needs to be analyzed to determine whether the array can be replaced with a ref or out parameter.", "int[] pos"), - Severity = DiagnosticSeverity.Warning, - Locations = - new[] { - new DiagnosticResultLocation("Test0.cs", 11, 53) - } - }; - - VerifyCSharpDiagnostic(test, expected); - } - - [Test] - public void TestDiagnostic_ParseChar_String_CharArray_Char() - { - var test = @" - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading.Tasks; - using System.Diagnostics; - - public class MyClass - { - public static bool ParseChar(string id, char[] pos, char ch) - { - int start = pos[0]; - pos[0] = PatternProps.SkipWhiteSpace(id, pos[0]); - if (pos[0] == id.Length || - id[pos[0]] != ch) - { - pos[0] = start; - return false; - } - ++pos[0]; - return true; - } - } - "; - - // We shouldn't trigger a warning on char[] - VerifyCSharpDiagnostic(test); - } - } -} diff --git a/tests/Lucene.Net.Tests.CodeAnalysis.Dev/TestLuceneDev1004_ArrayMethodReturnValueCSCodeAnalyzer.cs b/tests/Lucene.Net.Tests.CodeAnalysis.Dev/TestLuceneDev1004_ArrayMethodReturnValueCSCodeAnalyzer.cs deleted file mode 100644 index d9ab723..0000000 --- a/tests/Lucene.Net.Tests.CodeAnalysis.Dev/TestLuceneDev1004_ArrayMethodReturnValueCSCodeAnalyzer.cs +++ /dev/null @@ -1,93 +0,0 @@ -using Lucene.Net.CodeAnalysis.Dev; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; -using NUnit.Framework; -using TestHelper; - -namespace Lucene.Net.Tests.CodeAnalysis.Dev -{ - public class TestLuceneDev1004_ArrayMethodReturnValueCSCodeAnalyzer : DiagnosticVerifier - { - protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return new LuceneDev1004_ArrayMethodReturnValueCSCodeAnalyzer(); - } - - //No diagnostics expected to show up - [Test] - public void TestEmptyFile() - { - var test = @""; - - VerifyCSharpDiagnostic(test); - } - - [Test] - public void TestDiagnostic_GetVersionByteArrayFromCompactInt32_ByteArrayReturnType() - { - var test = @" - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading.Tasks; - using System.Diagnostics; - - public class MyClass - { - public static byte[] GetVersionByteArrayFromCompactInt32(int version) // ICU4N specific - Renamed from GetVersionByteArrayFromCompactInt - { - return new byte[] { - (byte)(version >> 24), - (byte)(version >> 16), - (byte)(version >> 8), - (byte)(version) - }; - } - } - "; - - var expected = new DiagnosticResult - { - Id = LuceneDev1004_ArrayMethodReturnValueCSCodeAnalyzer.DiagnosticId, - Message = string.Format("'{0}' return type needs to be analyzed to determine whether the array return value can be replaced with one or more out parameters or a return ValueTuple instead of an array to avoid the heap allocation.", "byte[]"), - Severity = DiagnosticSeverity.Warning, - Locations = - new[] { - new DiagnosticResultLocation("Test0.cs", 11, 27) - } - }; - - VerifyCSharpDiagnostic(test, expected); - } - - [Test] - public void TestDiagnostic_GetVersionCharArrayFromCompactInt32_CharArrayReturnType() - { - var test = @" - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading.Tasks; - using System.Diagnostics; - - public class MyClass - { - public static char[] GetVersionCharArrayFromCompactInt32(int version) - { - return new char[] { - (char)(version >> 24), - (char)(version >> 16), - (char)(version >> 8), - (char)(version) - }; - } - } - "; - - // We shouldn't trigger a warning on char[] - VerifyCSharpDiagnostic(test); - } - } -} diff --git a/tests/Lucene.Net.Tests.CodeAnalysis.Dev/Verifiers/CodeFixVerifier.cs b/tests/Lucene.Net.Tests.CodeAnalysis.Dev/Verifiers/CodeFixVerifier.cs deleted file mode 100644 index 9c01a0b..0000000 --- a/tests/Lucene.Net.Tests.CodeAnalysis.Dev/Verifiers/CodeFixVerifier.cs +++ /dev/null @@ -1,145 +0,0 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Formatting; -using NUnit.Framework; -using System.Collections.Generic; -using System.Linq; -using System.Threading; - -namespace TestHelper -{ - /* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - /// - /// Superclass of all Unit tests made for diagnostics with codefixes. - /// Contains methods used to verify correctness of codefixes - /// - public abstract partial class CodeFixVerifier : DiagnosticVerifier - { - /// - /// Returns the codefix being tested (C#) - to be implemented in non-abstract class - /// - /// The CodeFixProvider to be used for CSharp code - protected virtual CodeFixProvider GetCSharpCodeFixProvider() - { - return null; - } - - /// - /// Returns the codefix being tested (VB) - to be implemented in non-abstract class - /// - /// The CodeFixProvider to be used for VisualBasic code - protected virtual CodeFixProvider GetBasicCodeFixProvider() - { - return null; - } - - /// - /// Called to test a C# codefix when applied on the inputted string as a source - /// - /// A class in the form of a string before the CodeFix was applied to it - /// A class in the form of a string after the CodeFix was applied to it - /// Index determining which codefix to apply if there are multiple - /// A bool controlling whether or not the test will fail if the CodeFix introduces other warnings after being applied - protected void VerifyCSharpFix(string oldSource, string newSource, int? codeFixIndex = null, bool allowNewCompilerDiagnostics = false) - { - VerifyFix(LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer(), GetCSharpCodeFixProvider(), oldSource, newSource, codeFixIndex, allowNewCompilerDiagnostics); - } - - /// - /// Called to test a VB codefix when applied on the inputted string as a source - /// - /// A class in the form of a string before the CodeFix was applied to it - /// A class in the form of a string after the CodeFix was applied to it - /// Index determining which codefix to apply if there are multiple - /// A bool controlling whether or not the test will fail if the CodeFix introduces other warnings after being applied - protected void VerifyBasicFix(string oldSource, string newSource, int? codeFixIndex = null, bool allowNewCompilerDiagnostics = false) - { - VerifyFix(LanguageNames.VisualBasic, GetBasicDiagnosticAnalyzer(), GetBasicCodeFixProvider(), oldSource, newSource, codeFixIndex, allowNewCompilerDiagnostics); - } - - /// - /// General verifier for codefixes. - /// Creates a Document from the source string, then gets diagnostics on it and applies the relevant codefixes. - /// Then gets the string after the codefix is applied and compares it with the expected result. - /// Note: If any codefix causes new diagnostics to show up, the test fails unless allowNewCompilerDiagnostics is set to true. - /// - /// The language the source code is in - /// The analyzer to be applied to the source code - /// The codefix to be applied to the code wherever the relevant Diagnostic is found - /// A class in the form of a string before the CodeFix was applied to it - /// A class in the form of a string after the CodeFix was applied to it - /// Index determining which codefix to apply if there are multiple - /// A bool controlling whether or not the test will fail if the CodeFix introduces other warnings after being applied - private static void VerifyFix(string language, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string oldSource, string newSource, int? codeFixIndex, bool allowNewCompilerDiagnostics) - { - var document = CreateDocument(oldSource, language); - var analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(analyzer, new[] { document }); - var compilerDiagnostics = GetCompilerDiagnostics(document); - var attempts = analyzerDiagnostics.Length; - - for (int i = 0; i < attempts; ++i) - { - var actions = new List(); - var context = new CodeFixContext(document, analyzerDiagnostics[0], (a, d) => actions.Add(a), CancellationToken.None); - codeFixProvider.RegisterCodeFixesAsync(context).Wait(); - - if (!actions.Any()) - { - break; - } - - if (codeFixIndex != null) - { - document = ApplyFix(document, actions.ElementAt((int)codeFixIndex)); - break; - } - - document = ApplyFix(document, actions.ElementAt(0)); - analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(analyzer, new[] { document }); - - var newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, GetCompilerDiagnostics(document)); - - //check if applying the code fix introduced any new compiler diagnostics - if (!allowNewCompilerDiagnostics && newCompilerDiagnostics.Any()) - { - // Format and get the compiler diagnostics again so that the locations make sense in the output - document = document.WithSyntaxRoot(Formatter.Format(document.GetSyntaxRootAsync().Result, Formatter.Annotation, document.Project.Solution.Workspace)); - newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, GetCompilerDiagnostics(document)); - - Assert.IsTrue(false, - string.Format("Fix introduced new compiler diagnostics:\r\n{0}\r\n\r\nNew document:\r\n{1}\r\n", - string.Join("\r\n", newCompilerDiagnostics.Select(d => d.ToString())), - document.GetSyntaxRootAsync().Result.ToFullString())); - } - - //check if there are analyzer diagnostics left after the code fix - if (!analyzerDiagnostics.Any()) - { - break; - } - } - - //after applying all of the code fixes, compare the resulting string to the inputted one - var actual = GetStringFromDocument(document); - Assert.AreEqual(newSource, actual); - } - } -} diff --git a/tests/Lucene.Net.Tests.CodeAnalysis.Dev/Verifiers/DiagnosticVerifier.cs b/tests/Lucene.Net.Tests.CodeAnalysis.Dev/Verifiers/DiagnosticVerifier.cs deleted file mode 100644 index 3fe73c9..0000000 --- a/tests/Lucene.Net.Tests.CodeAnalysis.Dev/Verifiers/DiagnosticVerifier.cs +++ /dev/null @@ -1,288 +0,0 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Diagnostics; -using NUnit.Framework; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace TestHelper -{ - /* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - /// - /// Superclass of all Unit Tests for DiagnosticAnalyzers - /// - [TestFixture] - public abstract partial class DiagnosticVerifier - { - #region To be implemented by Test classes - /// - /// Get the CSharp analyzer being tested - to be implemented in non-abstract class - /// - protected virtual DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() - { - return null; - } - - /// - /// Get the Visual Basic analyzer being tested (C#) - to be implemented in non-abstract class - /// - protected virtual DiagnosticAnalyzer GetBasicDiagnosticAnalyzer() - { - return null; - } - #endregion - - #region Verifier wrappers - - /// - /// Called to test a C# DiagnosticAnalyzer when applied on the single inputted string as a source - /// Note: input a DiagnosticResult for each Diagnostic expected - /// - /// A class in the form of a string to run the analyzer on - /// DiagnosticResults that should appear after the analyzer is run on the source - protected void VerifyCSharpDiagnostic(string source, params DiagnosticResult[] expected) - { - VerifyDiagnostics(new[] { source }, LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer(), expected); - } - - /// - /// Called to test a VB DiagnosticAnalyzer when applied on the single inputted string as a source - /// Note: input a DiagnosticResult for each Diagnostic expected - /// - /// A class in the form of a string to run the analyzer on - /// DiagnosticResults that should appear after the analyzer is run on the source - protected void VerifyBasicDiagnostic(string source, params DiagnosticResult[] expected) - { - VerifyDiagnostics(new[] { source }, LanguageNames.VisualBasic, GetBasicDiagnosticAnalyzer(), expected); - } - - /// - /// Called to test a C# DiagnosticAnalyzer when applied on the inputted strings as a source - /// Note: input a DiagnosticResult for each Diagnostic expected - /// - /// An array of strings to create source documents from to run the analyzers on - /// DiagnosticResults that should appear after the analyzer is run on the sources - protected void VerifyCSharpDiagnostic(string[] sources, params DiagnosticResult[] expected) - { - VerifyDiagnostics(sources, LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer(), expected); - } - - /// - /// Called to test a VB DiagnosticAnalyzer when applied on the inputted strings as a source - /// Note: input a DiagnosticResult for each Diagnostic expected - /// - /// An array of strings to create source documents from to run the analyzers on - /// DiagnosticResults that should appear after the analyzer is run on the sources - protected void VerifyBasicDiagnostic(string[] sources, params DiagnosticResult[] expected) - { - VerifyDiagnostics(sources, LanguageNames.VisualBasic, GetBasicDiagnosticAnalyzer(), expected); - } - - /// - /// General method that gets a collection of actual diagnostics found in the source after the analyzer is run, - /// then verifies each of them. - /// - /// An array of strings to create source documents from to run the analyzers on - /// The language of the classes represented by the source strings - /// The analyzer to be run on the source code - /// DiagnosticResults that should appear after the analyzer is run on the sources - private static void VerifyDiagnostics(string[] sources, string language, DiagnosticAnalyzer analyzer, params DiagnosticResult[] expected) - { - var diagnostics = GetSortedDiagnostics(sources, language, analyzer); - VerifyDiagnosticResults(diagnostics, analyzer, expected); - } - - #endregion - - #region Actual comparisons and verifications - /// - /// Checks each of the actual Diagnostics found and compares them with the corresponding DiagnosticResult in the array of expected results. - /// Diagnostics are considered equal only if the DiagnosticResultLocation, Id, Severity, and Message of the DiagnosticResult match the actual diagnostic. - /// - /// The Diagnostics found by the compiler after running the analyzer on the source code - /// The analyzer that was being run on the sources - /// Diagnostic Results that should have appeared in the code - private static void VerifyDiagnosticResults(IEnumerable actualResults, DiagnosticAnalyzer analyzer, params DiagnosticResult[] expectedResults) - { - int expectedCount = expectedResults.Count(); - int actualCount = actualResults.Count(); - - if (expectedCount != actualCount) - { - string diagnosticsOutput = actualResults.Any() ? FormatDiagnostics(analyzer, actualResults.ToArray()) : " NONE."; - - Assert.IsTrue(false, - string.Format("Mismatch between number of diagnostics returned, expected \"{0}\" actual \"{1}\"\r\n\r\nDiagnostics:\r\n{2}\r\n", expectedCount, actualCount, diagnosticsOutput)); - } - - for (int i = 0; i < expectedResults.Length; i++) - { - var actual = actualResults.ElementAt(i); - var expected = expectedResults[i]; - - if (expected.Line == -1 && expected.Column == -1) - { - if (actual.Location != Location.None) - { - Assert.IsTrue(false, - string.Format("Expected:\nA project diagnostic with No location\nActual:\n{0}", - FormatDiagnostics(analyzer, actual))); - } - } - else - { - VerifyDiagnosticLocation(analyzer, actual, actual.Location, expected.Locations.First()); - var additionalLocations = actual.AdditionalLocations.ToArray(); - - if (additionalLocations.Length != expected.Locations.Length - 1) - { - Assert.IsTrue(false, - string.Format("Expected {0} additional locations but got {1} for Diagnostic:\r\n {2}\r\n", - expected.Locations.Length - 1, additionalLocations.Length, - FormatDiagnostics(analyzer, actual))); - } - - for (int j = 0; j < additionalLocations.Length; ++j) - { - VerifyDiagnosticLocation(analyzer, actual, additionalLocations[j], expected.Locations[j + 1]); - } - } - - if (actual.Id != expected.Id) - { - Assert.IsTrue(false, - string.Format("Expected diagnostic id to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", - expected.Id, actual.Id, FormatDiagnostics(analyzer, actual))); - } - - if (actual.Severity != expected.Severity) - { - Assert.IsTrue(false, - string.Format("Expected diagnostic severity to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", - expected.Severity, actual.Severity, FormatDiagnostics(analyzer, actual))); - } - - if (actual.GetMessage() != expected.Message) - { - Assert.IsTrue(false, - string.Format("Expected diagnostic message to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", - expected.Message, actual.GetMessage(), FormatDiagnostics(analyzer, actual))); - } - } - } - - /// - /// Helper method to VerifyDiagnosticResult that checks the location of a diagnostic and compares it with the location in the expected DiagnosticResult. - /// - /// The analyzer that was being run on the sources - /// The diagnostic that was found in the code - /// The Location of the Diagnostic found in the code - /// The DiagnosticResultLocation that should have been found - private static void VerifyDiagnosticLocation(DiagnosticAnalyzer analyzer, Diagnostic diagnostic, Location actual, DiagnosticResultLocation expected) - { - var actualSpan = actual.GetLineSpan(); - - Assert.IsTrue(actualSpan.Path == expected.Path || (actualSpan.Path != null && actualSpan.Path.Contains("Test0.") && expected.Path.Contains("Test.")), - string.Format("Expected diagnostic to be in file \"{0}\" was actually in file \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", - expected.Path, actualSpan.Path, FormatDiagnostics(analyzer, diagnostic))); - - var actualLinePosition = actualSpan.StartLinePosition; - - // Only check line position if there is an actual line in the real diagnostic - if (actualLinePosition.Line > 0) - { - if (actualLinePosition.Line + 1 != expected.Line) - { - Assert.IsTrue(false, - string.Format("Expected diagnostic to be on line \"{0}\" was actually on line \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", - expected.Line, actualLinePosition.Line + 1, FormatDiagnostics(analyzer, diagnostic))); - } - } - - // Only check column position if there is an actual column position in the real diagnostic - if (actualLinePosition.Character > 0) - { - if (actualLinePosition.Character + 1 != expected.Column) - { - Assert.IsTrue(false, - string.Format("Expected diagnostic to start at column \"{0}\" was actually at column \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", - expected.Column, actualLinePosition.Character + 1, FormatDiagnostics(analyzer, diagnostic))); - } - } - } - #endregion - - #region Formatting Diagnostics - /// - /// Helper method to format a Diagnostic into an easily readable string - /// - /// The analyzer that this verifier tests - /// The Diagnostics to be formatted - /// The Diagnostics formatted as a string - private static string FormatDiagnostics(DiagnosticAnalyzer analyzer, params Diagnostic[] diagnostics) - { - var builder = new StringBuilder(); - for (int i = 0; i < diagnostics.Length; ++i) - { - builder.AppendLine("// " + diagnostics[i].ToString()); - - var analyzerType = analyzer.GetType(); - var rules = analyzer.SupportedDiagnostics; - - foreach (var rule in rules) - { - if (rule != null && rule.Id == diagnostics[i].Id) - { - var location = diagnostics[i].Location; - if (location == Location.None) - { - builder.AppendFormat("GetGlobalResult({0}.{1})", analyzerType.Name, rule.Id); - } - else - { - Assert.IsTrue(location.IsInSource, - $"Test base does not currently handle diagnostics in metadata locations. Diagnostic in metadata: {diagnostics[i]}\r\n"); - - string resultMethodName = diagnostics[i].Location.SourceTree.FilePath.EndsWith(".cs") ? "GetCSharpResultAt" : "GetBasicResultAt"; - var linePosition = diagnostics[i].Location.GetLineSpan().StartLinePosition; - - builder.AppendFormat("{0}({1}, {2}, {3}.{4})", - resultMethodName, - linePosition.Line + 1, - linePosition.Character + 1, - analyzerType.Name, - rule.Id); - } - - if (i != diagnostics.Length - 1) - { - builder.Append(','); - } - - builder.AppendLine(); - break; - } - } - } - return builder.ToString(); - } - #endregion - } -} diff --git a/version.json b/version.json new file mode 100644 index 0000000..489db69 --- /dev/null +++ b/version.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "1.0.0-alpha.{height}", + "assemblyVersion": { + "precision": "major" + }, + "nuGetPackageVersion": { + "semVer": 2.0 + }, + "publicReleaseRefSpec": [ + "^refs/heads/main$", + "^refs/heads/master$", + "^refs/heads/release/v\\d+\\.\\d+\\.\\d+$" + ], + "cloudBuild": { + "buildNumber": { + "enabled": true + } + }, + "release": { + "branchName": "release/v{version}" + } +}